Envelope and headers
Every event arrives in the same envelope; only type and data change.
Envelope
{
"id": "evt_0199c3a4-adbe-7fc0-8b2c-475869708b30",
"type": "fin.invoice.paid",
"occurred_at": "2026-10-02T09:14:00Z",
"tenant": {
"id": "0199c39f-1a2b-7c3d-8e4f-5a6b7c8d9e01",
"slug": "northwind-transport"
},
"schema_version": "2026-05-16",
"data": {
"id": "0199c3a2-4b7e-7f30-9c2e-6a8d4f1b2c01",
"number": "INV-2026-00418",
"status": "paid",
"currency": "EUR",
"total_minor": 147500,
"paid_minor": 147500,
"balance_minor": 0
},
"metadata": {
"request_id": null,
"actor": { "type": "user", "id": "0199c38a-2536-7748-83a4-cfd0e1f20322" }
}
}
(data is shortened here; each event’s full payload is in the
event catalogue.)
data is the record as OfficeHero published it, with every *_at value in
RFC 3339 UTC. Fields that only mean something inside OfficeHero are never
sent: the company’s database schema name (identify the company by the
envelope’s tenant.id), storage keys, keyed hashes (such as nino_hmac) and
raw provider callback bodies.
| Field | Meaning |
|---|---|
id | The event id: evt_ + UUID. The same event keeps its id across retries and replays — deduplicate on it. |
type | The event type, e.g. fin.invoice.paid. |
occurred_at | When it happened, RFC 3339 UTC. |
tenant.id, tenant.slug | The company the event belongs to: join on id, show slug. There are no other tenant fields. |
schema_version | The payload version, 2026-05-16. |
data | The event payload. Its shape depends on type. |
metadata.request_id | For a write made through the Integrations API, the x-trace-id that request was answered with. Usually null otherwise. |
metadata.actor | Who caused it: {type, id}. api_key and the key’s id for a write made through the Integrations API — use it to recognise your own writes; user and a user id for a change made in the apps; system and null for automatic changes. |
Headers
| Header | Value |
|---|---|
Content-Type | application/json |
User-Agent | OfficeHero-Webhooks/1.0 |
X-OH-Signature | t=<unix seconds>,v1=<hex> — see Verifying signatures |
X-OH-Signature-Algorithm | HMAC-SHA256-RAW |
X-OH-Signature-Legacy | The pre-2026 signature, for receivers built before it changed. Sent until the date below. |
X-OH-Signature-Deprecation-Date | 2026-12-17 — when X-OH-Signature-Legacy stops |
OH-Event-Id | Same as the envelope id |
OH-Event-Type | Same as the envelope type |
OH-Event-Attempt | 1 for the first attempt, then 2, 3, … |
OH-Endpoint-Id | whe_ + your endpoint id |
OH-Version | Your endpoint’s pinned_version |
Idempotency-Key | Same as OH-Event-Id |
Header names are case-insensitive. Proxies and servers often change their
capitalisation on the way to your code — X-Oh-Signature, x-oh-signature,
Oh-Version — so look them up with your framework’s header accessor, not as
exact keys of a map.
Deliveries are never redirected: a 3xx answer counts as a failure.