Docs menu

Changelog

Additive changes ship continuously; anything breaking gets a new OH-Version and a deprecation notice here first.

Webhook delivery is live

2026-10-12 OH-Version 2026-05-16

OfficeHero now delivers integration webhooks in production. An endpoint receives the events it subscribes to that happen after it was created — signed, retried on the published schedule and replayable. Before announcing it we checked delivery end to end in production: a real delivery passed every verifier on this site, and each of them refused tampered, wrong-secret, stale and future copies.

  • Verifier helpers accept any header capitalisation. HTTP header names are case-insensitive, and servers and proxies change them on the way to your code: php-fpm’s getallheaders(), Go and many proxies hand over X-Oh-Signature, and Node lower-cases every name. The PHP ohVerifyDualSign looked only for X-OH-Signature and x-oh-signature, so it refused genuine deliveries read through getallheaders(). The TypeScript and Python …DualSign helpers had the same two-name lookup. All three now take the request’s whole header map and find both signature headers under any capitalisation. They also accept list values (PSR-7 getHeaders()), a header sent twice, and whitespace around a value.
  • If you copied the PHP helper, copy it again from Verifying signatures. Do the same for the TypeScript …DualSign helper unless you pass it Node’s lower-cased req.headers, and for the Python one if you pass it a plain dict instead of your framework’s request.headers. ohVerifySignature and the Go, Ruby, Java and C# verifiers work as before: they take the header value, so read it with your framework’s case-insensitive accessor, as their comments now say.
  • Every verifier reads the header the same way. The TypeScript ohVerifySignature now returns false for a missing header instead of throwing, and takes a list of values like the Python one. The Go verifier now refuses a t with a sign and an upper-case v1, and the Python one a t written in non-ASCII digits, as the others always did. Genuine deliveries pass exactly as before.
  • A delivered delivery reports no error. In GET /v1/webhook_endpoints/{id}/deliveries, error_class and error_detail describe the last failed attempt. A delivery that succeeds on a retry (or after a 429) now has both set to null. attempt still shows how many attempts it took. Deliveries that succeeded before this change keep the values they showed.

Integrations API and developer portal launch

2026-10-10 OH-Version 2026-05-16

The Integrations API is open on https://api.officehero.io with OH-Version: 2026-05-16, together with this portal.

  • Read invoices, payments, bills, purchase orders, work orders, vehicles, fuel logs, employees, leave requests, timesheets, tickets and document metadata. Write invoices: create and update drafts, and void issued invoices. A draft is issued — or discarded — in the Finance app.
  • Webhook endpoints can be created and managed through the API, for a catalogue of 112 event types (108 of them sent today), signed with HMAC-SHA256, with delivery history and replay. Delivery has not started yet: an entry here will say when it does.
  • 14 scopes you pick per API key in Settings → API keys.
  • Every *_at timestamp is RFC 3339 UTC (2026-09-14T08:30:00Z).
  • Each webhook delivery attempt is signed when it is sent, so retries pass a 300-second timestamp check.

Known limitations in this version, documented on each resource: lists have no filters or sort options; employee name, email, department and job title are always null, and so is a leave request’s reason; a document’s shared_with is always []; a vehicle’s last_seen_at and a timesheet’s regular_hours/overtime_hours are always null; a payment’s status is derived from its direction; invoice lines are not returned; document lists include deleted and quarantined files; webhook subscriptions to compliance, e-invoicing, DATEV, tachograph, dashcam and e-signature events need an int:* key, which the apps do not offer.