Quick answer
The ASHR.work API lives at /api/v1. Product metadata is open; your
own workspace data needs an API key carrying the right scope, sent as
Authorization: Bearer <key>. Reads are cursor-paginated, writes
are idempotent, and errors are RFC 9457 problem+json.
Quickstart
Create a key
Open Admin → Settings → Developer & API, create a key, tick the module permissions you need (e.g.leave:read,leave:write), and copy it — it's shown once.Call the API
Send it as a Bearer token. The base URL ishttps://ashr.work/api.Explore live
Open the API reference, click Authenticate, paste your key, and use Test Request on any endpoint.
# Read holidays (needs holidays:read)
curl -s https://ashr.work/api/v1/holidays \
-H "Authorization: Bearer dmk_live_…"
# Apply for leave (needs leave:write), idempotent
curl -s https://ashr.work/api/v1/leave-requests \
-H "Authorization: Bearer dmk_live_…" \
-H "Idempotency-Key: 6f1c…" \
-H "Content-Type: application/json" \
-d '{"employee_id":"…","leave_type_id":"…","start_date":"2026-09-01","end_date":"2026-09-03","days_count":3,"reason":"Family trip"}'
Scopes
Each key carries module:action scopes — leave:read, leave:write,
leave:approve, payroll:run, and so on. The console only offers the scopes for
modules your workspace has enabled. An endpoint returns 403 insufficient_scope
if the key is missing the scope it needs.
Pagination
List endpoints return { data, next_cursor, has_more }. Pass ?limit= (max 100)
and follow next_cursor with ?cursor= until has_more is false.
Idempotency & errors
Send an Idempotency-Key on writes so a retry never double-posts. Errors are
problem+json with a stable code and a request_id you can quote to support.
Every response carries X-Request-Id and RateLimit-* headers.
SDKs
Dependency-free TypeScript and Python SDKs wrap all of this
(list, get, applyLeave, whoami, …), and the spec at
/api/openapi.json imports into Postman, Insomnia or Swagger UI.
Frequently asked questions
- Do I need a key to read public data?
- No. Product and module metadata (/api/v1/health, /capabilities, /modules) is anonymous. A key is only needed for your tenant's data and for writes.
- How do I avoid double-posting a write on a retry?
- Send an Idempotency-Key header. If the same key is retried, the first response is replayed instead of running twice; reusing a key with a different body is a 409.
- What do errors look like?
- RFC 9457 problem+json with type, title, status, code, detail and request_id fields. Common codes are api_key_required (401), insufficient_scope (403), invalid_parameter (400) and rate_limited (429).
Related articles
How do I create API keys and use OAuth2?
Generate scoped API keys (shown once, hashed at rest), edit, rotate or revoke them; or register an OAuth2 client to exchange a client id + secret for short-lived access tokens with refresh.
How do webhooks work?
Register an endpoint and pick events; ASHR sends a signed POST when they happen. Verify the X-Demystify-Signature HMAC with the secret shown once, and reject stale or unsigned requests.