Download OpenAPI specification:
API-first multi-tenant HR service. The same API is used by the bundled dashboard, by master integrators that provision and operate many tenants, and by tenant-side machine integrations.
All authenticated requests send a bearer token:
Authorization: Bearer <token>
Three credential types are accepted (each operation lists which subset it allows):
| Scheme | Token format | Used by |
|---|---|---|
masterApiKey |
mh_live_… (env MASTER_API_KEY) |
The operator's backend, cross-tenant |
tenantApiKey |
mh_live_… (minted from dashboard) |
A tenant's own integrations |
userSession |
Auth-service access token (JWT) | The OurTeamManagement web app, on behalf of a user |
Tenant-scoped endpoints resolve the tenant org per caller type:
masterApiKey → X-Tenant-Id header (UUID).tenantApiKey → derived from the key; the header is ignored.userSession → X-Org-Id header, validated against the user's memberships.All data access is enforced by Postgres Row-Level Security — a missing or wrong tenant id never returns another tenant's data.
Master callers can pass X-Actor: {"id":"u_123","email":"a@b","name":"Ada"} (JSON) to attribute the request to a specific user in the audit log (e.g. the human in the operator's product who triggered the call). User-session callers cannot set X-Actor (the actor is taken from the session to prevent spoofing).
Writes (POST / PATCH / DELETE) require an Idempotency-Key header (any unique string per logical operation, typically a UUID). Replays within 24h return the cached response. Reusing a key with a different request body returns 409 conflict.
Every error response uses a single envelope:
{ "error": { "code": "not_found", "message": "...", "details": {} } }
Stable codes: bad_request, tenant_required, unauthorized, forbidden, not_found, conflict, internal_error. New codes are additive — clients should switch on code and treat unknown codes as a generic failure.
List endpoints use opaque cursors. Pass ?limit= (1–200, default 50) and ?cursor=<value> to page forward. The response is { items: [...], nextCursor: string | null } — nextCursor: null means you're at the end. Cursors are stable across writes (deletions skip rather than fail).
Each masterApiKey and tenantApiKey is limited to 600 requests / minute, burst 60/sec. Limits are returned on every response:
RateLimit-Limit — total per windowRateLimit-Remaining — left in windowRateLimit-Reset — seconds until resetExceeding the limit returns 429 too_many_requests. Retry with exponential backoff using the Retry-After header.
The major version is in the URL (/v1). Within a major version we only make additive changes (new fields, new operations, new error codes). Deprecations are announced at least 90 days in advance via the Deprecation and Sunset response headers (RFC 8594) and called out in apps/api/CHANGELOG.md.
Breaking changes only ever ship under a new major (/v2), and /v1 will run alongside it for at least 12 months.
Register a delivery URL via POST /v1/webhook-endpoints with the events you want — currently employee.created, employee.updated, employee.deleted, and (forthcoming) document.expiring. The signing secret is returned once at creation; use it to verify the Webhook-Signature header on every inbound delivery. The header value is t=<unix-seconds>,v1=<hex-hmac-sha256> of <t>.<raw-body>, the same format Stripe uses; the SDK exposes verifyWebhookSignature() for convenience.
Delivery semantics: each event is enqueued per subscribed endpoint with up to 8 attempts and exponential backoff (~1s → ~17h). The delivery audit log is queryable at /v1/webhook-deliveries, and any non-delivered row can be re-enqueued via /v1/webhook-deliveries/{id}/redeliver. Receivers must respond with any 2xx within 10s; non-2xx triggers a retry.
curl https://api.ourteammanagement.com/v1/employees \
-H 'Authorization: Bearer $MASTER_API_KEY' \
-H 'X-Tenant-Id: 11111111-2222-3333-4444-555555555555' \
-H 'Idempotency-Key: '"$(uuidgen)" \
-H 'Content-Type: application/json' \
-d '{"email":"ada@acme.com","firstName":"Ada","lastName":"Lovelace","country":"us","startDate":"2026-06-01"}'
Returns a cursor-paginated list of employees in the current tenant.
| status | string Enum: "onboarding" "active" "on_leave" "terminated" Example: status=active |
| managerId | string <uuid> |
| country | string Enum: "us" "de" |
| cursor | string |
| limit | integer [ 1 .. 200 ] Default: 50 Example: limit=50 |
| x-tenant-id | string <uuid> Tenant org id — required for root master and partner callers, ignored for tenant-key + user callers. |
| x-org-id | string <uuid> Tenant org id — required for end-user (auth-service) callers. |
| x-actor | string Optional JSON |
curl -X GET 'https://api.ourteammanagement.com/v1/employees?limit=50&status=active' \ -H 'Authorization: Bearer $MYHR_API_KEY' \ -H 'X-Tenant-Id: 11111111-2222-3333-4444-555555555555'
{- "items": [
- {
- "id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
- "orgId": "11111111-2222-3333-4444-555555555555",
- "externalId": "emp_4271",
- "email": "ada@acme.com",
- "firstName": "Ada",
- "lastName": "Lovelace",
- "preferredName": null,
- "jobTitle": "Staff Engineer",
- "department": "Engineering",
- "managerId": null,
- "country": "us",
- "startDate": "2026-06-01",
- "endDate": null,
- "status": "active",
- "createdAt": "2026-05-04T12:00:00.000Z",
- "updatedAt": "2026-05-04T12:00:00.000Z"
}
], - "nextCursor": null
}Creates a new employee in the current tenant.
| x-tenant-id | string <uuid> Tenant org id — required for root master and partner callers, ignored for tenant-key + user callers. |
| x-org-id | string <uuid> Tenant org id — required for end-user (auth-service) callers. |
| x-actor | string Optional JSON |
| idempotency-key required | string [ 1 .. 200 ] characters Required on all writes (POST/PATCH/DELETE). Replays return the cached response. Reusing a key with a different body returns 409. |
| externalId | string [ 1 .. 200 ] characters |
| email required | string <email> |
| firstName required | string [ 1 .. 200 ] characters |
| lastName required | string [ 1 .. 200 ] characters |
| preferredName | string <= 200 characters |
| jobTitle | string <= 200 characters |
| department | string <= 200 characters |
| managerId | string <uuid> |
| country required | string Enum: "us" "de" |
| startDate required | string^\d{4}-\d{2}-\d{2}$ |
| endDate | string^\d{4}-\d{2}-\d{2}$ |
| status | string Default: "onboarding" Enum: "onboarding" "active" "on_leave" "terminated" |
{- "email": "ada@acme.com",
- "firstName": "Ada",
- "lastName": "Lovelace",
- "country": "us",
- "startDate": "2026-06-01",
- "jobTitle": "Staff Engineer",
- "department": "Engineering"
}{- "id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
- "orgId": "11111111-2222-3333-4444-555555555555",
- "externalId": "emp_4271",
- "email": "ada@acme.com",
- "firstName": "Ada",
- "lastName": "Lovelace",
- "preferredName": null,
- "jobTitle": "Staff Engineer",
- "department": "Engineering",
- "managerId": null,
- "country": "us",
- "startDate": "2026-06-01",
- "endDate": null,
- "status": "onboarding",
- "createdAt": "2026-05-04T12:00:00.000Z",
- "updatedAt": "2026-05-04T12:00:00.000Z"
}Returns a single employee by id, scoped to the current tenant.
| id required | string <uuid> Example: aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee |
| x-tenant-id | string <uuid> Tenant org id — required for root master and partner callers, ignored for tenant-key + user callers. |
| x-org-id | string <uuid> Tenant org id — required for end-user (auth-service) callers. |
| x-actor | string Optional JSON |
curl -X GET 'https://api.ourteammanagement.com/v1/employees/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee' \ -H 'Authorization: Bearer $MYHR_API_KEY' \ -H 'X-Tenant-Id: 11111111-2222-3333-4444-555555555555'
{- "id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
- "orgId": "11111111-2222-3333-4444-555555555555",
- "externalId": "emp_4271",
- "email": "ada@acme.com",
- "firstName": "Ada",
- "lastName": "Lovelace",
- "preferredName": null,
- "jobTitle": "Staff Engineer",
- "department": "Engineering",
- "managerId": null,
- "country": "us",
- "startDate": "2026-06-01",
- "endDate": null,
- "status": "onboarding",
- "createdAt": "2026-05-04T12:00:00.000Z",
- "updatedAt": "2026-05-04T12:00:00.000Z"
}Partially updates an employee. Only provided fields are changed.
| id required | string <uuid> Example: aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee |
| x-tenant-id | string <uuid> Tenant org id — required for root master and partner callers, ignored for tenant-key + user callers. |
| x-org-id | string <uuid> Tenant org id — required for end-user (auth-service) callers. |
| x-actor | string Optional JSON |
| idempotency-key required | string [ 1 .. 200 ] characters Required on all writes (POST/PATCH/DELETE). Replays return the cached response. Reusing a key with a different body returns 409. |
| externalId | string [ 1 .. 200 ] characters |
string <email> | |
| firstName | string [ 1 .. 200 ] characters |
| lastName | string [ 1 .. 200 ] characters |
| preferredName | string <= 200 characters |
| jobTitle | string <= 200 characters |
| department | string <= 200 characters |
| managerId | string <uuid> |
| country | string Enum: "us" "de" |
| startDate | string^\d{4}-\d{2}-\d{2}$ |
| endDate | string^\d{4}-\d{2}-\d{2}$ |
| status | string Default: "onboarding" Enum: "onboarding" "active" "on_leave" "terminated" |
{- "jobTitle": "Principal Engineer",
- "status": "active"
}{- "id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
- "orgId": "11111111-2222-3333-4444-555555555555",
- "externalId": "emp_4271",
- "email": "ada@acme.com",
- "firstName": "Ada",
- "lastName": "Lovelace",
- "preferredName": null,
- "jobTitle": "Principal Engineer",
- "department": "Engineering",
- "managerId": null,
- "country": "us",
- "startDate": "2026-06-01",
- "endDate": null,
- "status": "active",
- "createdAt": "2026-05-04T12:00:00.000Z",
- "updatedAt": "2026-05-04T12:00:00.000Z"
}Soft-deletes an employee and anonymizes PII in place. A scheduled job performs hard erasure after retention deadlines pass.
| id required | string <uuid> Example: aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee |
| x-tenant-id | string <uuid> Tenant org id — required for root master and partner callers, ignored for tenant-key + user callers. |
| x-org-id | string <uuid> Tenant org id — required for end-user (auth-service) callers. |
| x-actor | string Optional JSON |
| idempotency-key required | string [ 1 .. 200 ] characters Required on all writes (POST/PATCH/DELETE). Replays return the cached response. Reusing a key with a different body returns 409. |
curl -X DELETE 'https://api.ourteammanagement.com/v1/employees/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee' \ -H 'Authorization: Bearer $MYHR_API_KEY' \ -H 'X-Tenant-Id: 11111111-2222-3333-4444-555555555555' \ -H 'Idempotency-Key: '"$(uuidgen)"
{- "id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
- "deletedAt": "2026-05-04T12:00:00.000Z"
}Returns the full employee record. A follow-up will also bundle contracts and documents into a zip.
| id required | string <uuid> Example: aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee |
| x-tenant-id | string <uuid> Tenant org id — required for root master and partner callers, ignored for tenant-key + user callers. |
| x-org-id | string <uuid> Tenant org id — required for end-user (auth-service) callers. |
| x-actor | string Optional JSON |
curl -X GET 'https://api.ourteammanagement.com/v1/employees/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/export' \ -H 'Authorization: Bearer $MYHR_API_KEY' \ -H 'X-Tenant-Id: 11111111-2222-3333-4444-555555555555'
{- "employee": {
- "id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
- "orgId": "11111111-2222-3333-4444-555555555555",
- "externalId": "emp_4271",
- "email": "ada@acme.com",
- "firstName": "Ada",
- "lastName": "Lovelace",
- "preferredName": null,
- "jobTitle": "Staff Engineer",
- "department": "Engineering",
- "managerId": null,
- "country": "us",
- "startDate": "2026-06-01",
- "endDate": null,
- "status": "onboarding",
- "createdAt": "2026-05-04T12:00:00.000Z",
- "updatedAt": "2026-05-04T12:00:00.000Z"
}, - "exportedAt": "2026-05-04T12:00:00.000Z"
}Tenant orgs. Root master, partner, and end-user creation; root master + partner read (partners see only their own orgs).
Lists tenant orgs. Root master sees every org on the deployment. Partner callers see only the orgs they themselves provisioned (RLS-isolated from every other partner).
| cursor | string |
| limit | integer [ 1 .. 200 ] Default: 50 Example: limit=50 |
| x-actor | string Optional JSON |
curl -X GET 'https://api.ourteammanagement.com/v1/orgs?limit=50' \ -H 'Authorization: Bearer $MYHR_API_KEY'
{- "items": [
- {
- "id": "11111111-2222-3333-4444-555555555555",
- "name": "Acme Inc",
- "region": "eu",
- "status": "active",
- "partnerId": null,
- "createdAt": "2026-05-04T12:00:00.000Z",
- "updatedAt": "2026-05-04T12:00:00.000Z"
}
], - "nextCursor": null
}Provisions a new tenant org. Root master and partner callers provision on behalf of a tenant (partner-created orgs are tagged with the partner id). End-user callers create their own org and become owner.
| x-tenant-id | string <uuid> Tenant org id — required for root master and partner callers, ignored for tenant-key + user callers. |
| x-org-id | string <uuid> Tenant org id — required for end-user (auth-service) callers. |
| x-actor | string Optional JSON |
| idempotency-key required | string [ 1 .. 200 ] characters Required on all writes (POST/PATCH/DELETE). Replays return the cached response. Reusing a key with a different body returns 409. |
| name required | string [ 1 .. 200 ] characters |
| region | string Default: "eu" Enum: "eu" "us" |
{- "name": "Acme Inc",
- "region": "eu"
}{- "id": "11111111-2222-3333-4444-555555555555",
- "name": "Acme Inc",
- "region": "eu",
- "status": "active",
- "partnerId": null,
- "createdAt": "2026-05-04T12:00:00.000Z",
- "updatedAt": "2026-05-04T12:00:00.000Z"
}Returns a single tenant org by id. Root master sees every org; partners see only the orgs they themselves provisioned (404 otherwise).
| id required | string <uuid> Example: 11111111-2222-3333-4444-555555555555 |
| x-actor | string Optional JSON |
curl -X GET 'https://api.ourteammanagement.com/v1/orgs/11111111-2222-3333-4444-555555555555' \ -H 'Authorization: Bearer $MYHR_API_KEY'
{- "id": "11111111-2222-3333-4444-555555555555",
- "name": "Acme Inc",
- "region": "eu",
- "status": "active",
- "partnerId": null,
- "createdAt": "2026-05-04T12:00:00.000Z",
- "updatedAt": "2026-05-04T12:00:00.000Z"
}Partially updates a tenant org. Root master can update any org; partners can update only orgs they themselves provisioned.
| id required | string <uuid> Example: 11111111-2222-3333-4444-555555555555 |
| idempotency-key required | string [ 1 .. 200 ] characters Required on all writes (POST/PATCH/DELETE). Replays return the cached response. Reusing a key with a different body returns 409. |
| x-actor | string Optional JSON |
| name | string [ 1 .. 200 ] characters |
| region | string Default: "eu" Enum: "eu" "us" |
| status | string Enum: "active" "suspended" "deleted" |
{- "name": "Acme Holdings GmbH"
}{- "id": "11111111-2222-3333-4444-555555555555",
- "name": "Acme Holdings GmbH",
- "region": "eu",
- "status": "active",
- "partnerId": null,
- "createdAt": "2026-05-04T12:00:00.000Z",
- "updatedAt": "2026-05-04T12:00:00.000Z"
}Returns all active members of the resolved org with their role and identity.
| x-tenant-id | string <uuid> Tenant org id — required for root master and partner callers, ignored for tenant-key + user callers. |
| x-org-id | string <uuid> Tenant org id — required for end-user (auth-service) callers. |
| x-actor | string Optional JSON |
curl -X GET 'https://api.ourteammanagement.com/v1/members' \ -H 'Authorization: Bearer $MYHR_API_KEY' \ -H 'X-Tenant-Id: 11111111-2222-3333-4444-555555555555'
{- "items": [
- {
- "membershipId": "cccccccc-dddd-eeee-ffff-aaaaaaaaaaaa",
- "userId": "u_01HXYZUSER00000000000000",
- "email": "ada@acme.com",
- "name": "Ada Lovelace",
- "role": "admin",
- "joinedAt": "2026-05-04T12:00:00.000Z"
}
]
}Creates an invitation record and returns the acceptance URL plus the plaintext token. Owner/admin only for user callers; master + tenant_key callers bypass the role check.
| x-tenant-id | string <uuid> Tenant org id — required for root master and partner callers, ignored for tenant-key + user callers. |
| x-org-id | string <uuid> Tenant org id — required for end-user (auth-service) callers. |
| x-actor | string Optional JSON |
| idempotency-key required | string [ 1 .. 200 ] characters Required on all writes (POST/PATCH/DELETE). Replays return the cached response. Reusing a key with a different body returns 409. |
| email required | string <email> |
| role required | string Enum: "owner" "admin" "manager" "member" |
{- "email": "newhire@acme.com",
- "role": "admin"
}{- "id": "00000000-1111-2222-3333-444444444444",
- "orgId": "11111111-2222-3333-4444-555555555555",
- "email": "newhire@acme.com",
- "role": "admin",
- "expiresAt": "2026-05-11T12:00:00.000Z",
- "createdAt": "2026-05-04T12:00:00.000Z",
- "token": "inv_4f3c1aa9e2b14d8e9c0f7d6b5e2a1c8d"
}Returns invitations for the resolved org that haven't been accepted or revoked.
| x-tenant-id | string <uuid> Tenant org id — required for root master and partner callers, ignored for tenant-key + user callers. |
| x-org-id | string <uuid> Tenant org id — required for end-user (auth-service) callers. |
| x-actor | string Optional JSON |
curl -X GET 'https://api.ourteammanagement.com/v1/invitations' \ -H 'Authorization: Bearer $MYHR_API_KEY' \ -H 'X-Tenant-Id: 11111111-2222-3333-4444-555555555555'
{- "items": [
- {
- "id": "00000000-1111-2222-3333-444444444444",
- "orgId": "11111111-2222-3333-4444-555555555555",
- "email": "newhire@acme.com",
- "role": "admin",
- "expiresAt": "2026-05-11T12:00:00.000Z",
- "createdAt": "2026-05-04T12:00:00.000Z"
}
]
}Exchanges an invitation token for a membership. The caller's email must match the email on the invitation.
| idempotency-key required | string [ 1 .. 200 ] characters Required on all writes (POST/PATCH/DELETE). Replays return the cached response. Reusing a key with a different body returns 409. |
| token required | string non-empty |
{- "token": "inv_4f3c1aa9e2b14d8e9c0f7d6b5e2a1c8d"
}{- "id": "cccccccc-dddd-eeee-ffff-aaaaaaaaaaaa",
- "orgId": "11111111-2222-3333-4444-555555555555",
- "userId": "u_01HXYZUSER00000000000000",
- "role": "admin",
- "createdAt": "2026-05-04T12:00:00.000Z"
}Creates a new tenant-scoped API key. The plaintext value is returned once and never again — store it immediately.
| x-tenant-id | string <uuid> Tenant org id — required for root master and partner callers, ignored for tenant-key + user callers. |
| x-org-id | string <uuid> Tenant org id — required for end-user (auth-service) callers. |
| x-actor | string Optional JSON |
| idempotency-key required | string [ 1 .. 200 ] characters Required on all writes (POST/PATCH/DELETE). Replays return the cached response. Reusing a key with a different body returns 409. |
| name required | string [ 1 .. 200 ] characters |
{- "name": "Production server (read+write)"
}{- "id": "bbbbbbbb-cccc-dddd-eeee-ffffffffffff",
- "name": "Production server (read+write)",
- "prefix": "mh_live_4f3c1aa9e2b1",
- "scope": "tenant",
- "lastUsedAt": null,
- "createdAt": "2026-05-04T12:00:00.000Z",
- "key": "mh_live_4f3c1aa9e2b14d8e9c0f7d6b5e2a1c8d"
}Returns metadata for every tenant-scoped key minted for this org. Plaintext keys are never returned.
| x-tenant-id | string <uuid> Tenant org id — required for root master and partner callers, ignored for tenant-key + user callers. |
| x-org-id | string <uuid> Tenant org id — required for end-user (auth-service) callers. |
| x-actor | string Optional JSON |
curl -X GET 'https://api.ourteammanagement.com/v1/api-keys' \ -H 'Authorization: Bearer $MYHR_API_KEY' \ -H 'X-Tenant-Id: 11111111-2222-3333-4444-555555555555'
{- "items": [
- {
- "id": "bbbbbbbb-cccc-dddd-eeee-ffffffffffff",
- "name": "Production server (read+write)",
- "prefix": "mh_live_4f3c1aa9e2b1",
- "scope": "tenant",
- "lastUsedAt": "2026-05-04T12:00:00.000Z",
- "createdAt": "2026-05-04T12:00:00.000Z"
}
]
}Returns the currently authenticated end user. User callers only.
curl -X GET 'https://api.ourteammanagement.com/v1/me' \ -H 'Authorization: Bearer $MYHR_API_KEY'
{- "id": "u_01HXYZUSER00000000000000",
- "email": "ada@acme.com",
- "name": "Ada Lovelace",
- "isSuperAdmin": false,
- "createdAt": "2026-05-04T12:00:00.000Z"
}Returns one row per org the caller has a membership in, denormalised with the org and the caller's role.
curl -X GET 'https://api.ourteammanagement.com/v1/me/orgs' \ -H 'Authorization: Bearer $MYHR_API_KEY'
{- "items": [
- {
- "org": {
- "id": "11111111-2222-3333-4444-555555555555",
- "name": "Acme Inc",
- "region": "eu",
- "status": "active",
- "partnerId": null,
- "createdAt": "2026-05-04T12:00:00.000Z",
- "updatedAt": "2026-05-04T12:00:00.000Z"
}, - "role": "admin",
- "joinedAt": "2026-05-04T12:00:00.000Z"
}
]
}Webhook endpoint management + delivery audit. Manage where OurTeamManagement delivers events with /v1/webhook-endpoints/*; inspect or replay attempts with /v1/webhook-deliveries/*. The event payloads OurTeamManagement actually POSTs are documented under the Webhooks section.
Emitted on every successful POST /v1/employees. The payload is the same shape returned by the create endpoint (minus encrypted-at-rest fields).
| id required | string <uuid> Unique event id. Use as the deduplication key. |
| type required | string Value: "employee.created" Event type. Always present; safe to switch on. |
| createdAt required | string <date-time> |
| orgId required | string <uuid> Tenant org the event applies to. |
| data required | object |
{- "id": "ev_01HXYZ0000000000000001",
- "type": "employee.created",
- "createdAt": "2026-05-04T12:00:00.000Z",
- "orgId": "11111111-2222-3333-4444-555555555555",
- "data": {
- "id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
- "email": "ada@acme.com",
- "firstName": "Ada",
- "lastName": "Lovelace",
- "status": "active"
}
}Emitted after a successful PATCH /v1/employees/{id} whenever any persisted field changes. Re-emitted only if the post-write state differs.
| id required | string <uuid> Unique event id. Use as the deduplication key. |
| type required | string Value: "employee.updated" Event type. Always present; safe to switch on. |
| createdAt required | string <date-time> |
| orgId required | string <uuid> Tenant org the event applies to. |
| data required | object |
{- "id": "ev_01HXYZ0000000000000001",
- "type": "employee.updated",
- "createdAt": "2026-05-04T12:00:00.000Z",
- "orgId": "11111111-2222-3333-4444-555555555555",
- "data": {
- "id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
- "email": "ada@acme.com",
- "firstName": "Ada",
- "lastName": "Lovelace",
- "status": "on_leave"
}
}Emitted when an employee is soft-deleted. PII has already been redacted at this point — only id, orgId, and deletedAt are guaranteed.
| id required | string <uuid> Unique event id. Use as the deduplication key. |
| type required | string Value: "employee.deleted" Event type. Always present; safe to switch on. |
| createdAt required | string <date-time> |
| orgId required | string <uuid> Tenant org the event applies to. |
| data required | object |
{- "id": "ev_01HXYZ0000000000000001",
- "type": "employee.deleted",
- "createdAt": "2026-05-04T12:00:00.000Z",
- "orgId": "11111111-2222-3333-4444-555555555555",
- "data": {
- "id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
- "deletedAt": "2026-05-04T12:00:00.000Z"
}
}Emitted at 30 / 14 / 7 / 1 days before document.expiresAt so the integrator can notify the employee or HR. Each window fires exactly once per document.
| id required | string <uuid> Unique event id. Use as the deduplication key. |
| type required | string Value: "document.expiring" Event type. Always present; safe to switch on. |
| createdAt required | string <date-time> |
| orgId required | string <uuid> Tenant org the event applies to. |
| data required | object |
{- "id": "ev_01HXYZ0000000000000001",
- "type": "document.expiring",
- "createdAt": "2026-05-04T12:00:00.000Z",
- "orgId": "11111111-2222-3333-4444-555555555555",
- "data": {
- "documentId": "dddddddd-eeee-ffff-aaaa-bbbbbbbbbbbb",
- "employeeId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
- "kind": "visa",
- "expiresAt": "2026-06-04T00:00:00.000Z",
- "daysUntilExpiry": 30
}
}Register a URL to receive event deliveries. The signing secret is returned once at creation; store it immediately — every delivery is signed with it via HMAC-SHA256 in the Webhook-Signature header. Subsequent reads return the endpoint metadata only.
| x-tenant-id | string <uuid> Tenant org id — required for root master and partner callers, ignored for tenant-key + user callers. |
| x-org-id | string <uuid> Tenant org id — required for end-user (auth-service) callers. |
| x-actor | string Optional JSON |
| idempotency-key required | string [ 1 .. 200 ] characters Required on all writes (POST/PATCH/DELETE). Replays return the cached response. Reusing a key with a different body returns 409. |
| url required | string <uri> ^https:\/\/ |
| events required | Array of strings non-empty Items Enum: "employee.created" "employee.updated" "employee.deleted" "document.expiring" |
{- "events": [
- "employee.created",
- "employee.updated"
]
}{- "id": "eeeeeeee-aaaa-bbbb-cccc-dddddddddddd",
- "orgId": "11111111-2222-3333-4444-555555555555",
- "events": [
- "employee.created",
- "employee.updated"
], - "isActive": true,
- "createdAt": "2026-05-04T12:00:00.000Z",
- "updatedAt": "2026-05-04T12:00:00.000Z",
- "secret": "whsec_4f3c1aa9e2b14d8e9c0f7d6b5e2a1c8d4f3c1aa9e2b14d8e9c0f7d6b5e2a1c8d"
}| x-tenant-id | string <uuid> Tenant org id — required for root master and partner callers, ignored for tenant-key + user callers. |
| x-org-id | string <uuid> Tenant org id — required for end-user (auth-service) callers. |
| x-actor | string Optional JSON |
curl -X GET 'https://api.ourteammanagement.com/v1/webhook-endpoints' \ -H 'Authorization: Bearer $MYHR_API_KEY' \ -H 'X-Tenant-Id: 11111111-2222-3333-4444-555555555555'
{- "items": [
- {
- "id": "eeeeeeee-aaaa-bbbb-cccc-dddddddddddd",
- "orgId": "11111111-2222-3333-4444-555555555555",
- "events": [
- "employee.created",
- "employee.updated"
], - "isActive": true,
- "createdAt": "2026-05-04T12:00:00.000Z",
- "updatedAt": "2026-05-04T12:00:00.000Z"
}
]
}| id required | string <uuid> Example: eeeeeeee-aaaa-bbbb-cccc-dddddddddddd |
| x-tenant-id | string <uuid> Tenant org id — required for root master and partner callers, ignored for tenant-key + user callers. |
| x-org-id | string <uuid> Tenant org id — required for end-user (auth-service) callers. |
| x-actor | string Optional JSON |
curl -X GET 'https://api.ourteammanagement.com/v1/webhook-endpoints/eeeeeeee-aaaa-bbbb-cccc-dddddddddddd' \ -H 'Authorization: Bearer $MYHR_API_KEY' \ -H 'X-Tenant-Id: 11111111-2222-3333-4444-555555555555'
{- "id": "eeeeeeee-aaaa-bbbb-cccc-dddddddddddd",
- "orgId": "11111111-2222-3333-4444-555555555555",
- "events": [
- "employee.created",
- "employee.updated"
], - "isActive": true,
- "createdAt": "2026-05-04T12:00:00.000Z",
- "updatedAt": "2026-05-04T12:00:00.000Z"
}Update the URL, subscribed events, or active state. The signing secret is not changed — see rotateWebhookEndpointSecret to rotate.
| id required | string <uuid> Example: eeeeeeee-aaaa-bbbb-cccc-dddddddddddd |
| x-tenant-id | string <uuid> Tenant org id — required for root master and partner callers, ignored for tenant-key + user callers. |
| x-org-id | string <uuid> Tenant org id — required for end-user (auth-service) callers. |
| x-actor | string Optional JSON |
| idempotency-key required | string [ 1 .. 200 ] characters Required on all writes (POST/PATCH/DELETE). Replays return the cached response. Reusing a key with a different body returns 409. |
| url | string <uri> ^https:\/\/ |
| events | Array of strings non-empty Items Enum: "employee.created" "employee.updated" "employee.deleted" "document.expiring" |
| isActive | boolean |
{- "events": [
- "employee.created",
- "employee.updated",
- "employee.deleted"
]
}{- "id": "eeeeeeee-aaaa-bbbb-cccc-dddddddddddd",
- "orgId": "11111111-2222-3333-4444-555555555555",
- "events": [
- "employee.created",
- "employee.updated",
- "employee.deleted"
], - "isActive": true,
- "createdAt": "2026-05-04T12:00:00.000Z",
- "updatedAt": "2026-05-04T12:00:00.000Z"
}Removes the endpoint and cascades pending deliveries. Returns 204 with no body. Existing WebhookDelivery rows remain for audit.
| id required | string <uuid> Example: eeeeeeee-aaaa-bbbb-cccc-dddddddddddd |
| x-tenant-id | string <uuid> Tenant org id — required for root master and partner callers, ignored for tenant-key + user callers. |
| x-org-id | string <uuid> Tenant org id — required for end-user (auth-service) callers. |
| x-actor | string Optional JSON |
| idempotency-key required | string [ 1 .. 200 ] characters Required on all writes (POST/PATCH/DELETE). Replays return the cached response. Reusing a key with a different body returns 409. |
curl -X DELETE 'https://api.ourteammanagement.com/v1/webhook-endpoints/eeeeeeee-aaaa-bbbb-cccc-dddddddddddd' \ -H 'Authorization: Bearer $MYHR_API_KEY' \ -H 'X-Tenant-Id: 11111111-2222-3333-4444-555555555555' \ -H 'Idempotency-Key: '"$(uuidgen)"
{- "error": {
- "code": "bad_request",
- "message": "managerId does not reference an employee in this tenant"
}
}Mints a fresh signing secret and returns it once. The previous secret stops working immediately — coordinate the cutover with the receiver before calling.
| id required | string <uuid> Example: eeeeeeee-aaaa-bbbb-cccc-dddddddddddd |
| x-tenant-id | string <uuid> Tenant org id — required for root master and partner callers, ignored for tenant-key + user callers. |
| x-org-id | string <uuid> Tenant org id — required for end-user (auth-service) callers. |
| x-actor | string Optional JSON |
| idempotency-key required | string [ 1 .. 200 ] characters Required on all writes (POST/PATCH/DELETE). Replays return the cached response. Reusing a key with a different body returns 409. |
curl -X POST 'https://api.ourteammanagement.com/v1/webhook-endpoints/eeeeeeee-aaaa-bbbb-cccc-dddddddddddd/rotate-secret' \ -H 'Authorization: Bearer $MYHR_API_KEY' \ -H 'X-Tenant-Id: 11111111-2222-3333-4444-555555555555' \ -H 'Idempotency-Key: '"$(uuidgen)"
{- "id": "eeeeeeee-aaaa-bbbb-cccc-dddddddddddd",
- "orgId": "11111111-2222-3333-4444-555555555555",
- "events": [
- "employee.created",
- "employee.updated"
], - "isActive": true,
- "createdAt": "2026-05-04T12:00:00.000Z",
- "updatedAt": "2026-05-04T12:00:00.000Z",
- "secret": "whsec_4f3c1aa9e2b14d8e9c0f7d6b5e2a1c8d4f3c1aa9e2b14d8e9c0f7d6b5e2a1c8d"
}Returns the recent deliveries for this org, newest first. Useful for diagnostics — every attempt is recorded with the response code, body (truncated to 4 KiB), and any error.
| endpointId | string <uuid> |
| eventType | string Enum: "employee.created" "employee.updated" "employee.deleted" "document.expiring" |
| status | string Enum: "pending" "in_progress" "delivered" "failed_retrying" "failed_permanent" Example: status=delivered |
| cursor | string |
| limit | integer [ 1 .. 200 ] Default: 50 Example: limit=50 |
| x-tenant-id | string <uuid> Tenant org id — required for root master and partner callers, ignored for tenant-key + user callers. |
| x-org-id | string <uuid> Tenant org id — required for end-user (auth-service) callers. |
| x-actor | string Optional JSON |
curl -X GET 'https://api.ourteammanagement.com/v1/webhook-deliveries?limit=50&status=delivered' \ -H 'Authorization: Bearer $MYHR_API_KEY' \ -H 'X-Tenant-Id: 11111111-2222-3333-4444-555555555555'
{- "items": [
- {
- "id": "ffffffff-eeee-dddd-cccc-bbbbbbbbbbbb",
- "orgId": "11111111-2222-3333-4444-555555555555",
- "endpointId": "eeeeeeee-aaaa-bbbb-cccc-dddddddddddd",
- "eventId": "11112222-3333-4444-5555-666677778888",
- "eventType": "employee.created",
- "status": "delivered",
- "attempts": 1,
- "maxAttempts": 8,
- "lastResponseCode": 200,
- "lastResponseBody": "ok",
- "lastError": null,
- "lastAttemptAt": "2026-05-04T12:00:00.000Z",
- "nextAttemptAt": null,
- "deliveredAt": "2026-05-04T12:00:00.000Z",
- "createdAt": "2026-05-04T12:00:00.000Z"
}
], - "nextCursor": null
}| id required | string <uuid> Example: ffffffff-eeee-dddd-cccc-bbbbbbbbbbbb |
| x-tenant-id | string <uuid> Tenant org id — required for root master and partner callers, ignored for tenant-key + user callers. |
| x-org-id | string <uuid> Tenant org id — required for end-user (auth-service) callers. |
| x-actor | string Optional JSON |
curl -X GET 'https://api.ourteammanagement.com/v1/webhook-deliveries/ffffffff-eeee-dddd-cccc-bbbbbbbbbbbb' \ -H 'Authorization: Bearer $MYHR_API_KEY' \ -H 'X-Tenant-Id: 11111111-2222-3333-4444-555555555555'
{- "id": "ffffffff-eeee-dddd-cccc-bbbbbbbbbbbb",
- "orgId": "11111111-2222-3333-4444-555555555555",
- "endpointId": "eeeeeeee-aaaa-bbbb-cccc-dddddddddddd",
- "eventId": "11112222-3333-4444-5555-666677778888",
- "eventType": "employee.created",
- "status": "delivered",
- "attempts": 1,
- "maxAttempts": 8,
- "lastResponseCode": 200,
- "lastResponseBody": "ok",
- "lastError": null,
- "lastAttemptAt": "2026-05-04T12:00:00.000Z",
- "nextAttemptAt": null,
- "deliveredAt": "2026-05-04T12:00:00.000Z",
- "createdAt": "2026-05-04T12:00:00.000Z"
}Resets the delivery's attempt counter to zero and enqueues a fresh job. The same eventId is used so consumers can deduplicate. Already-delivered rows are not redelivered (returns 409).
| id required | string <uuid> Example: ffffffff-eeee-dddd-cccc-bbbbbbbbbbbb |
| x-tenant-id | string <uuid> Tenant org id — required for root master and partner callers, ignored for tenant-key + user callers. |
| x-org-id | string <uuid> Tenant org id — required for end-user (auth-service) callers. |
| x-actor | string Optional JSON |
| idempotency-key required | string [ 1 .. 200 ] characters Required on all writes (POST/PATCH/DELETE). Replays return the cached response. Reusing a key with a different body returns 409. |
curl -X POST 'https://api.ourteammanagement.com/v1/webhook-deliveries/ffffffff-eeee-dddd-cccc-bbbbbbbbbbbb/redeliver' \ -H 'Authorization: Bearer $MYHR_API_KEY' \ -H 'X-Tenant-Id: 11111111-2222-3333-4444-555555555555' \ -H 'Idempotency-Key: '"$(uuidgen)"
{- "id": "ffffffff-eeee-dddd-cccc-bbbbbbbbbbbb",
- "orgId": "11111111-2222-3333-4444-555555555555",
- "endpointId": "eeeeeeee-aaaa-bbbb-cccc-dddddddddddd",
- "eventId": "11112222-3333-4444-5555-666677778888",
- "eventType": "employee.created",
- "status": "pending",
- "attempts": 0,
- "maxAttempts": 8,
- "lastResponseCode": 200,
- "lastResponseBody": "ok",
- "lastError": null,
- "lastAttemptAt": "2026-05-04T12:00:00.000Z",
- "nextAttemptAt": null,
- "deliveredAt": null,
- "createdAt": "2026-05-04T12:00:00.000Z"
}Cross-tenant org list for OurTeamManagement ops humans (is_super_admin = true). Master + tenant-key callers are rejected — they have their own paths.
| cursor | string |
| limit | integer [ 1 .. 200 ] Default: 50 Example: limit=50 |
curl -X GET 'https://api.ourteammanagement.com/v1/superadmin/orgs?limit=50' \ -H 'Authorization: Bearer $MYHR_API_KEY'
{- "items": [
- {
- "id": "11111111-2222-3333-4444-555555555555",
- "name": "Acme Inc",
- "region": "eu",
- "status": "active",
- "partnerId": null,
- "createdAt": "2026-05-04T12:00:00.000Z",
- "updatedAt": "2026-05-04T12:00:00.000Z"
}
], - "nextCursor": null
}curl -X GET 'https://api.ourteammanagement.com/' \ -H 'Authorization: Bearer $MYHR_API_KEY'
{- "name": "OurTeamManagement API",
- "version": "0.0.1",
}