Skip to content

Webhooks

Connect a public HTTPS receiver to the events your organisation needs. Ovee signs each notification; your receiver verifies it and uses the related GET API to retrieve current information.

  1. Create an API access key as an organisation administrator.
  2. Deploy a public HTTPS receiver. Private network addresses, loopback destinations, redirects, URL credentials, and fragments are not supported.
  3. In Organisation Settings → APIs → Webhooks, create a subscription with your receiver URL and selected event types. You can also use the subscription API below.
  4. Copy the signing secret once and save it in your receiver’s secret store. It is different from your Ovee API key.
  5. Select Send test, verify the signature, and check delivery history. Tests make one attempt. Use a real subscribed event when testing retries.

The following reference describes the current delivery contract. See the API reference for request and response schemas.

Manage API access keys and webhook subscriptions under Organisation Settings → APIs, at /org-settings/apis. The previous /org-settings/apiAccessKeys URL redirects there.

Use an existing Ovee API Access Key:

Authorization: Bearer <ovee-api-access-key>

The key’s bound organisation determines the subscription’s organisation. The caller must still be an admin of that organisation. The UI uses the same endpoints with the signed-in admin’s authentication. Subscriptions belong to the organisation, not to a particular access key.

All endpoints below start with /api/webhooks:

MethodEndpointPurpose
GET/capabilitiesDelivery/signing configuration status
GET/event-typesAvailable event names, descriptions and related API templates
GET/subscriptionsList this organisation’s subscriptions
POST/subscriptionsCreate a subscription; returns the signing secret once
GET/subscriptions/{id}Get subscription settings; never returns secrets
PATCH/subscriptions/{id}Edit name, URL, events, filters or enabled state
DELETE/subscriptions/{id}Unsubscribe and cancel queued deliveries
POST/subscriptions/{id}/rotate-secretGenerate a new signing secret, returned once
POST/subscriptions/{id}/testQueue a signed test, including for paused subscriptions
GET/subscriptions/{id}/deliveries?limit=50&before={cursor}Delivery history; maximum page size 100
GET/subscriptions/{id}/deliveries/{deliveryId}Notification payload and attempt history
POST/subscriptions/{id}/deliveries/{deliveryId}/replayQueue a failed delivery again, retaining the event ID
POST /api/webhooks/subscriptions
Content-Type: application/json
Authorization: Bearer <ovee-api-access-key>
{
"name": "Business integration",
"url": "https://integration.example.com/ovee-events",
"eventTypes": [
"partner.compliance.submitted",
"partner.compliance.approved",
"partner.compliance.status_changed",
"pool.created",
"person.association.created",
"person.association.type_changed",
"job.created",
"job.state_changed"
],
"associationTypes": ["EMPLOYEE", "TRUSTED"],
"enabled": true
}

associationTypes is optional. Omit it or use [] for all association types. It affects association events only. Supported values are EMPLOYEE, TRUSTED, LINKED, PENDING, and BLACKLISTED. Creation/type changes match the new type; removal matches the previous type.

PATCH replaces any supplied event/filter arrays and preserves omitted fields. At least one supported event is required. PATCH { "enabled": false } pauses the endpoint. PATCH { "enabled": true } resumes it and resets the consecutive failure count. Paused endpoints do not collect new events, while previously queued deliveries remain queued. URL edits apply to queued deliveries as well as future events.

Subscription and delivery IDs from other organisations return 404. No subscription, listing or history response exposes stored signing secrets. Deleting a subscription retains history for the retention period, but prevents replay. An already in-flight request may finish after unsubscribe.

GroupEventsRelated GET API
Partner organisationspartner.created, partner.updated, partner.removed/api/org/{orgId}/partners/{partnerOrgId}/preview
Partner onboardingpartner.onboarding.requested, partner.onboarding.accepted, partner.onboarding.declined, partner.onboarding.expired/api/org/{orgId}/partners/onboarding-requests/{requestId}
Compliance completionpartner.compliance.completion_changed/api/org-compliance/org/{partnerOrgId}?partnershipId={partnershipId}
Compliance packspartner.compliance.submitted, partner.compliance.approved, partner.compliance.conditionally_approved, partner.compliance.rejected/api/org-compliance/org/{partnerOrgId}?partnershipId={partnershipId}
Compliance evidence/statuspartner.compliance.evidence_updated, partner.compliance.status_changedSame compliance API
Organisation-owned poolspool.created, pool.updated, pool.deleted/api/pool/{poolId}
Pool membershippool.person_added, pool.person_removed/api/pool/{poolId}
Person associationsperson.association.created, person.association.type_changed, person.association.removed/api/org/person/{personId}
Onboardingperson.onboarding.started, person.onboarding.completed, person.offboarding.completed/api/org/person/{personId} or /api/org/person/{personId}/onboarding-records
Jobsjob.created, job.updated, job.state_changed, job.request_status_changed, job.deleted/api/job/{jobId}
Job applications/candidatesjob.application_submitted, job.candidate_state_changed/api/job/{jobId}/candidates

Partner compliance notifications go only to the organisation requesting the evidence. Where both organisations request evidence, each receives notifications for its own direction of the relationship. Evidence notifications cover applicable text, bank-detail and attachment changes; they never contain the evidence itself.

Approval and overall compliance are separate: an approved pack can subsequently become expired. Status is recalculated after relevant changes and checked hourly for date-driven expiry, even when nobody opens Ovee. This uses stored evidence and register results and does not initiate external register lookups.

Association creation means the first of the supported association types was granted. Moving Pending → Trusted → Employee produces type-change notifications. Automatically identified candidates do not count as applications; entering INTERESTED does, including reapplication.

Jobs are scoped to organisation-owned records. Private user/team-owned jobs and private/team pools are excluded. Operational state and recruitment requestStatus have distinct events. updated represents relevant persisted configuration changes, not analytics refreshes or unchanged saves. Only committed changes are notified; intermediate saves within the same transaction are combined. There is no historical backfill when a subscription is enabled. Deletion notifications have identifiers and links, but the referenced GET may subsequently return 404.

Potential future additions include workstream role provisioning, association approval requests/decisions, sharing invitations/acceptance, qualification verification, and upcoming document expiry notifications. These are not currently selectable events.

{
"id": "event-uuid",
"type": "partner.compliance.submitted",
"schemaVersion": 1,
"occurredAt": "2026-10-30T01:00:00Z",
"orgId": "requesting-org-id",
"resource": { "type": "partner", "id": "partner-org-id" },
"data": {
"partnerOrgId": "partner-org-id",
"partnershipId": "partnership-id",
"previousState": "OPEN",
"newState": "SUBMITTED_FOR_APPROVAL"
},
"links": [
{
"rel": "resource",
"method": "GET",
"href": "/api/org-compliance/org/partner-org-id?partnershipId=partnership-id"
}
]
}

API links contain the actual IDs and query parameters. Resolve href against the same Ovee base URL used by your integration, and fetch it with an API access key for orgId. The GET returns current information, which may have changed since the event occurred. Webhooks do not grant extra API permissions. Bank account details, documents, profile contents, free-text notes and decision reasons are not sent in event payloads.

Requests include:

Content-Type: application/json
X-Ovee-Event-Id: <stable-event-id>
X-Ovee-Event-Type: pool.created
X-Ovee-Delivery-Id: <delivery-id>
X-Ovee-Signature: t=<unix-seconds>,v1=<hex-hmac-sha256>

The signing secret is separate from the Ovee API key. Treat the entire returned whsec_... string as the UTF-8 HMAC key. Compute HMAC-SHA256 over the UTF-8 bytes of timestamp + "." + rawRequestBody, and compare hex signatures in constant time. Validate a five-minute timestamp tolerance. Verify the raw bytes before parsing or reformatting JSON.

During signing-secret rotation, the previous secret remains valid for 24 hours and the header contains two v1 signatures. Accept a match against any v1 value using a currently trusted signing secret. Every retry has a fresh signing timestamp; the event ID and event payload remain stable.

The receiver should verify, durably enqueue/deduplicate by event ID, and promptly return any 2xx response. Fetch full records in its own background processing. Delivery is at least once; ordering is not guaranteed. A worker crash after the receiver accepts a request can cause a duplicate. Replay retains the event ID but uses a new delivery ID, so receivers that intentionally reprocess a replay must account for their own deduplication policy.

The business action only writes an event and delivery entries in its MongoDB transaction. It does not call the remote webhook. A separate bounded worker sends HTTP after commit; receiver latency, timeouts and retries cannot hold up the triggering action. Rollbacks cannot produce deliverable notifications.

Each subscription has at most one active sender lease. Worker leases recover after process failure. Failed requests retry at event-relative offsets of 1 minute, 5 minutes, 15 minutes, 1 hour, 3 hours, 6 hours, 12 hours, and 24 hours. Requests have a ten-second overall deadline, including bounded DNS resolution and connection/TLS establishment. A valid Retry-After can delay the next attempt within the 24-hour window.

Five consecutive deliveries exhausting retries pause the endpoint. A successful delivery resets this count. Resume it in APIs settings or through PATCH, then replay failed deliveries as needed. Expired queued delivery windows are recorded as failures without issuing a stale HTTP request. Test deliveries make one attempt and do not affect automatic-pause counters.

Delivery statuses are PENDING, SUCCEEDED, FAILED, and CANCELLED. Retain failed delivery details for diagnosis and replay. Terminal delivery/event history expires after 30 days; pending entries are not expired by retention cleanup.

For the complete partner workflow, see the partner compliance How-to Guide. Onboarding events notify the requesting customer; acceptance is an action for the invited organisation administrator. Completion notifications carry identifiers, completion state and the last recorded completion timestamp. Bank details, document contents and signed storage URLs are never included. Fetch current authorised state after each event; ordering is not guaranteed.

This helper verifies the signature on the unmodified Buffer received from the HTTP request. Capture raw bytes before JSON middleware. Provide your current signing secret and, during rotation, the previous secret from your secret store.

import { createHmac, timingSafeEqual } from 'node:crypto';
function verifyOveeSignature(rawBody, header, secrets, now = Date.now()) {
if (!Buffer.isBuffer(rawBody) || typeof header !== 'string') return false;
const parts = header.split(',').map(part => part.trim());
const timestamps = parts.filter(part => part.startsWith('t='));
if (timestamps.length !== 1) return false;
const timestamp = timestamps[0].slice(2);
if (!/^\d+$/.test(timestamp)) return false;
const seconds = Number(timestamp);
if (!Number.isSafeInteger(seconds) || Math.abs(now / 1000 - seconds) > 300) return false;
const signatures = parts.filter(part => /^v1=[a-fA-F0-9]{64}$/.test(part)).map(part => Buffer.from(part.slice(3), 'hex'));
return secrets.some(secret => {
const expected = createHmac('sha256', secret)
.update(timestamp + '.', 'utf8')
.update(rawBody)
.digest();
return signatures.some(signature => timingSafeEqual(signature, expected));
});
}

After successful verification, parse the JSON, durably enqueue the notification, and deduplicate by its id. Only acknowledge with 2xx after durable acceptance. A duplicate should receive a successful acknowledgement without repeating business effects.

SymptomNext step
No notificationCheck selected event types, optional association filters, organisation ownership, and whether the subscription is enabled. There is no historical backfill.
Signature mismatchVerify the original bytes; use the full whsec_... string as the UTF-8 key, check clock drift, and accept any matching v1 signature during rotation.
Test succeeds but real events failInspect delivery history, response statuses, and receiver processing. Tests do not exercise retries.
Subscription automatically pausedFix the receiver, resume the subscription, then replay failed deliveries as needed.
Linked GET failsUse the Ovee application origin, not the receiver origin, with an API key for the event’s organisation. Check resource permissions and whether the record was deleted.

Do not forward the Ovee API key to a webhook receiver URL. Resolve event resource links against your configured Ovee application origin.