1. Overview
The Public Pull API is a read-only HTTPS API mounted at /v1/*. It is designed for enterprise integrations where push delivery (outbound webhooks) isn’t a fit — for example, batch CRM syncs, data-warehouse loaders, or iPaaS recipes that prefer a polling model.
Every response is scoped to the tenant that owns the access token. Cross-tenant requests return 404 uniformly — there is no existence leak.
What you can pull: cases, documents (with extracted data), case decisions, screening hits, and a unified event feed that mirrors our outbound webhook payloads.
2. Authentication
Mint a client_id and client_secret in Compliance Configuration → Public Pull API. The secret is shown once — store it in your CRM/iPaaS secret manager.
Exchange the credentials for a 1-hour Bearer token via the OAuth2 client-credentials grant:
curl -X POST https://www.firstmilelabs.com/v1/oauth/token \
-H "Content-Type: application/json" \
-d '{
"grant_type": "client_credentials",
"client_id": "pk_live_abc123",
"client_secret": "sk_live_xxxxxxxxxxxx"
}'
# → { "access_token": "eyJhbGciOi…", "token_type": "Bearer",
# "expires_in": 3600, "scope": "read:cases read:documents read:events read:decisions read:screening" }Pass the token as Authorization: Bearer … on every API call. Tokens are signed JWTs; we honour them until expiry, but you can also rotate immediately by revoking the client.
Scopes: read:cases, read:documents, read:events, read:decisions, read:screening. Subset at creation time if you want to follow least-privilege.
3. Pagination & rate limits
List endpoints return at most limit rows (default 50, max 200) in data, plus an opaque nextCursor and a boolean hasMore. Pass the cursor back verbatim on the next call:
GET /v1/cases?limit=100&cursor=eyJ0Ijoi…Default rate limit is 120 requests / minute / client (the token endpoint is limited to 30 req/min per client). When throttled we return 429 with a Retry-After header in seconds.
4. Cases
GET /v1/cases returns the case list. Filters: status, updatedSince. GET /v1/cases/{caseId} returns a single case including its current decision and risk band, and /v1/cases/{caseId}/decisions and /v1/cases/{caseId}/screening expose the decision and screening histories.
curl https://www.firstmilelabs.com/v1/cases?limit=50 \
-H "Authorization: Bearer eyJhbGciOi…"
# → { "data": [ { "caseId": "CS-2026-00471", "companyName": "Acme Ltd",
# "status": "approved", "updatedAt": "2026-05-14T09:18:42Z", … } ],
# "nextCursor": "eyJ0Ijoi…", "hasMore": true }5. Documents
GET /v1/documents lists every document across the tenant (filter by caseId or status). GET /v1/documents/{id} returns the full record including the structured extractedData JSON produced by AI document intelligence — perfect for hydrating your CRM with the company name, registration number, directors, and so on.
6. Event feed (pull alternative to outbound webhooks)
GET /v1/events mirrors the payloads we send via outbound webhooks, deduped by eventId. Use it when push delivery is impractical (firewalled environments, batch ETL, polling recipes). The shape of data is identical to the corresponding webhook event.
curl "https://www.firstmilelabs.com/v1/events?event_type=case.decision.made&limit=100" \
-H "Authorization: Bearer eyJhbGciOi…"
# → { "data": [ { "eventId": "evt_…", "eventType": "case.decision.made", "eventVersion": 1,
# "occurredAt": "2026-05-14T09:18:42Z", "data": { … } } ],
# "nextCursor": "eyJ0Ijoi…", "hasMore": false }
#
# The shape of `data` for each `eventType` is defined by the per-event
# JSON Schemas at /schemas/webhooks/<eventType>.v1.json — identical to
# what you receive on push delivery.For a full payload reference, see the Outbound Webhook Guide.
7. Versioning policy
The API namespace is /v1/*. We follow the same additive compatibility policy as our outbound webhooks: new fields may be added at any time, and consumers must ignore unknown fields. Breaking changes ship under a new /v2/* namespace; /v1/* remains supported for the announced sunset window.
8. OpenAPI spec
The full machine-readable spec lives at /v1/openapi.json. Drop it into Postman, Insomnia, or your code-generator of choice to scaffold a typed client in minutes.