API Basics
Everything you need before making your first call to the Oho REST API: where to send requests, how to authenticate, and the shape of what comes back. These conventions apply to every endpoint, so the Workers & Credentials tutorial and the per-endpoint API Reference assume you've read this page.
Base URLs
Pick the environment you're testing against:
| Environment | Base URL |
|---|---|
| Local | http://localhost:8080/openapi/v1 |
| Staging | https://api.staging.ohohub.com/openapi/v1 |
| Production | https://api.ohohub.com/openapi/v1 |
The examples throughout these guides use an $OHO_BASE variable so you can switch environments
in one place:
export OHO_BASE="http://localhost:8080/openapi/v1"
export OHO_TOKEN="<your-access-token>"
Authentication
Every request carries a bearer token in the Authorization header:
curl -sS "$OHO_BASE/workers" \
-H "Authorization: Bearer $OHO_TOKEN"
Generate a token from Settings → Access Tokens in the Oho app. The token is authorized
per entity type — calls to /workers need access to the worker entity, calls to
/credentials need access to the credential entity.
Treat your token like a password. Don't commit it to source control or paste it into shared docs — set it as an environment variable as shown above.
For the full picture — token scopes and operations, the 401 vs 403 distinction, safe
rotation, and the server-side oauth integration endpoints — see the
Authentication & Tokens deep-dive.
Identifiers
Resources are addressed by a synthetic, prefixed ID generated by the server at create time:
| Entity | ID shape | Example |
|---|---|---|
| Worker | wkr_<id> | wkr_V1StGXR8Z5jdHi6B |
| Credential | cred_<id> | cred_8x2Kf9aQ4mN7pLrT |
| Applicant | app_<id> | app_Qz7yT2mB |
You never supply these — the server mints them and returns them in the create response and the
Location header. Internal URNs are never exposed. To find a worker by your own upstream HRIS
identifier instead, use GET /workers?externalId=....
Response envelope
Single-resource reads and writes return a data object with a meta companion:
{
"data": {
"id": "wkr_V1StGXR8Z5jdHi6B",
"type": "worker",
"attributes": { "...": "..." }
},
"meta": { "requestId": "f3c1a0e2-..." }
}
List endpoints return a data array and paging metadata:
{
"data": [{ "id": "wkr_V1StGXR8Z5jdHi6B", "type": "worker", "...": "..." }],
"meta": { "page": 1, "pageSize": 25, "total": 1, "requestId": "..." }
}
pageSize defaults to 25 and is clamped to a maximum of 100.
Soft deletes
DELETE is a soft delete — it sets deleted: true and preserves the record and its history.
Soft-deleted resources are excluded from lists unless you pass includeDeleted=true.
Request tracing
Every response carries a server-generated meta.requestId, echoed in the X-Request-Id response
header. Quote it in support tickets — it's how Oho finds your call in its logs. If you send your
own X-Request-Id (a UUID or 26-character ULID), the server keeps its own id as canonical and
echoes yours back in a separate X-Client-Request-Id header so you can line the two systems up.
Errors
All errors carry an error field. Structured failures (conflict, validation, malformed JSON) put
a stable category in error and the detail in message; conflicts add a machine-readable code.
Simpler failures put the detail straight in error with no message/code. Branch on the HTTP
status and code — never on text:
{
"error": "Conflict",
"message": "credential is already linked to applicant app_Qz7yT2mB; use /transfer to replace",
"code": "ALREADY_LINKED"
}
| Status | Meaning |
|---|---|
200 | OK |
201 | Created (create endpoints; includes a Location header) |
204 | No Content (successful soft delete) |
400 | Validation error or unparseable filter/path value |
401 | Missing or invalid token |
403 | Authenticated, but not authorized for that entity type |
404 | Resource not found |
409 | Conflict with stored state (code: ALREADY_LINKED, OWNER_MISMATCH, DUPLICATE_ID) |
429 | Rate limited — sets a Retry-After header |
For the full picture — conflict recovery, safe retries, idempotency, and matching async webhook deliveries back to a request — see Errors, Retries & Idempotency.
Try it live
Each endpoint in the API Reference has a built-in "Try it" panel, and a
live Swagger UI is served at /openapi/swagger-ui/index.html (group openapi-v1).
Ready? Walk through a complete flow in the Workers & Credentials tutorial.