Fetch Requests Tutorial
A fetch request asks an existing worker to supply a credential you don't yet hold — a new First Aid certificate, a renewed registration number, a missing document. The worker receives an email with a secure upload link and submits the details; the request tracks their progress through to completion.
This is a short, copy-paste flow against the REST API: create a request, track it, then nudge, re-issue, or cancel it. Each step links to its full per-endpoint reference.
Read API Basics first — it covers base URLs, the Authorization header, IDs,
the response envelope, and error shapes that every step here relies on. The examples assume
$OHO_BASE and $OHO_TOKEN are set, and that you already have a worker (see the
Workers & Credentials tutorial).
Fetch requests carry their own ID shape and authorize against the captureRequest entity type
(your token needs access to it):
| Resource | ID shape | Example |
|---|---|---|
| Fetch request | cap_<id> | cap_V1StGXR8Z5jdHi6B |
1. Create the request
POST /fetch-requests with the target worker and what you're asking for. Identify the worker by
exactly one of worker.id (the wkr_ ID) or worker.externalId (your upstream HRIS id).
List the credentials you want in requestedCredentialTypes. Optionally add a message to the
worker and an expiryDays for the upload link. See Create fetch request
for the full field list.
curl -sS -X POST "$OHO_BASE/fetch-requests" \
-H "Authorization: Bearer $OHO_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"worker": { "id": "wkr_V1StGXR8Z5jdHi6B" },
"requestedCredentialTypes": ["FIRST_AID", "WWCC_NSW"],
"credentialGuidingTexts": { "FIRST_AID": "Please upload the certificate, not the wallet card." },
"message": "Hi Jordan — please send through your current First Aid and WWCC.",
"expiryDays": 14
}'
The server returns 201 with the generated cap_ ID and a Location header:
{
"data": {
"id": "cap_V1StGXR8Z5jdHi6B",
"type": "fetchRequest",
"attributes": {
"displayName": "Fetch Request — Jordan Avery — 2026-06-29",
"worker": { "id": "wkr_V1StGXR8Z5jdHi6B" },
"message": "Hi Jordan — please send through your current First Aid and WWCC.",
"requestedCredentialTypes": ["FIRST_AID", "WWCC_NSW"],
"credentialGuidingTexts": {
"FIRST_AID": "Please upload the certificate, not the wallet card."
},
"delivery": {
"emailAddress": "jordan.avery@example.com",
"tokenExpiresAt": "2026-07-13T02:14:07Z",
"remindersSent": 0
},
"lifecycle": {
"status": "INITIATED",
"initiatedAt": "2026-06-29T02:14:07Z"
}
}
},
"meta": { "requestId": "f3c1a0e2-..." }
}
Save the ID for the steps that follow:
export REQ="cap_V1StGXR8Z5jdHi6B"
requestedCredentialTypes takes the same codes as the rest of the platform (WWCC, Blue Card,
teacher registration, AHPRA, NDIS, First Aid, and more). Fetch the live list from
List supported credential types. For more complex asks — AND/OR
groups, exemptions, or ban checks — send a structured requirements block instead of the flat
list; see Create fetch request.
Provide exactly one worker identifier and exactly one requirements form — requestedCredentialTypes
or requirements, not both. Violations return 400.
2. Track the request
GET /fetch-requests/{requestId} returns the same envelope, with lifecycle.status reflecting
where the worker is up to. See Get fetch request.
curl -sS "$OHO_BASE/fetch-requests/$REQ" -H "Authorization: Bearer $OHO_TOKEN"
A request moves through these statuses:
| Status | Meaning |
|---|---|
INITIATED | Created; email queued |
SENT | Email delivered to the worker |
OPENED | Worker opened the upload link |
PARTIALLY_COMPLETED | Some, but not all, requested credentials supplied |
SUBMITTED | Worker submitted everything requested |
COMPLETED | All requested verifiables have been checked |
EXPIRED | Upload link lapsed before completion |
CANCELLED | Withdrawn by your team (step 6) |
To see what the worker has supplied so far, call
List submitted credentials at
GET /fetch-requests/{requestId}/submissions.
3. List and find requests
GET /fetch-requests lists requests. Filter by status, by workerId, or free-text query
(matched across worker name and email); page with page and pageSize. Full parameter list:
List fetch requests.
# Everything still awaiting a response
curl -sS "$OHO_BASE/fetch-requests?status=SENT" \
-H "Authorization: Bearer $OHO_TOKEN"
# All requests for one worker
curl -sS "$OHO_BASE/fetch-requests?workerId=wkr_V1StGXR8Z5jdHi6B" \
-H "Authorization: Bearer $OHO_TOKEN"
Returns the standard list envelope with data and a paging meta block. Soft-deleted requests
are excluded unless you pass includeDeleted=true.
4. Send a reminder
Worker gone quiet? POST /fetch-requests/{requestId}/remind increments delivery.remindersSent
on the existing request. It does not change the status, token, or expiry — the worker keeps the
same link. See Send reminder.
curl -sS -X POST "$OHO_BASE/fetch-requests/$REQ/remind" \
-H "Authorization: Bearer $OHO_TOKEN"
Returns 200 with the updated request.
Oho's scheduler sends reminder emails on its own: any request sitting in SENT/OPENED past the
reminder window (default 7 days, configurable) gets a reminder, up to a cap (default 3). It does
this by re-issuing the request through the INITIATED state, which is what actually dispatches the
email. The manual endpoint above bumps the counter for your own tracking; reach for resend
(step 5) when you want to force a fresh email to go out now.
5. Resend (re-issue the link)
If the link has expired — or you want to start the clock fresh — POST /fetch-requests/{requestId}/resend
mints a new capture token, extends the expiry, and resets the status to INITIATED. Use the
optional expiryDays query parameter to set the new window (default 14). See
Resend fetch request.
curl -sS -X POST "$OHO_BASE/fetch-requests/$REQ/resend?expiryDays=7" \
-H "Authorization: Bearer $OHO_TOKEN"
Returns 200 with the request reset to INITIATED and a refreshed delivery.tokenExpiresAt.
Remind records a nudge (remindersSent++) against the worker's existing link without changing
its state. Resend issues a new link, extends the expiry, and restarts the lifecycle at
INITIATED — which is what triggers a fresh email to go out. Reach for resend once a request has
expired or when you need an email sent immediately.
6. Cancel the request
Asked in error, or no longer needed? POST /fetch-requests/{requestId}/cancel withdraws it and
sets the status to CANCELLED. The worker's link stops working. See
Cancel fetch request.
curl -sS -X POST "$OHO_BASE/fetch-requests/$REQ/cancel" \
-H "Authorization: Bearer $OHO_TOKEN"
Returns 200 with the request in status CANCELLED.
Cancelling keeps the request on the worker's history as a withdrawn ask. To remove it from lists
entirely, soft-delete it with DELETE /fetch-requests/{requestId} — the
record and its history are preserved and resurface with includeDeleted=true.
Where to go next
- API Reference: Fetch Requests — every field, including bulk create,
structured
requirements, submissions, and the completed-request PDF report - Send a Fetch Request — the same flow from the Oho app
- Webhooks — receive events as workers respond