Event types & payloads
The 12 Hoursmith webhook event types, signed JSON envelope, client resource snapshots, and minimal private payout events.
Every webhook delivery is a JSON envelope describing a single event. This page lists the event
types Hoursmith emits and documents the envelope and its data payload.
Event types
Hoursmith emits 12 event types. There are no wildcard subscriptions in v1 — subscribe to each type you want.
| Event type | Description |
|---|---|
invoice.sent | An invoice was sent to a client. |
invoice.paid | An invoice was marked paid. |
invoice.payment_failed | A payment attempt on an invoice failed. |
time_entry.created | A time entry was created. |
task.completed | A task was marked complete. |
task.created | A task was created. |
client.created | A client was created. |
project.created | A project was created. |
team_bill.approved | An internal compensation bill was approved. |
team_bill.voided | An internal compensation bill was voided. |
team_payment.recorded | An external team payment was recorded; Hoursmith did not move funds. |
team_payment.reversed | A team payment was reversed in the ledger. |
The envelope
Every delivery has the same top-level shape:
{
"id": "4f9c2a1e-....",
"type": "invoice.paid",
"created": 1717603200,
"orgId": "00000000-0000-0000-0000-000000000000",
"livemode": true,
"data": {
"invoice": { "/* full GET /api/v1/invoices/{id} shape */": true },
"payment": { "/* ... */": true }
}
}| Field | Type | Description |
|---|---|---|
id | string (UUID) | Unique id for this event. All retries of one event share this id — use it to dedupe. |
type | string | The event type, e.g. invoice.paid. |
created | number | When the event was created, as a Unix timestamp in seconds. |
orgId | string (UUID) | Your workspace id. |
livemode | boolean | true only in production. Test events are false. |
data | object | Resource snapshot(s) for the event — see below. |
The created field is the event creation time. It is not the signature timestamp — that lives
in the t= value of the Hoursmith-Signature header. See
Verify signatures.
The data object
For the original client/work events below, data contains a snapshot of the resource the event is about. Each resource has the same shape as
its API GET response — for example, the invoice object matches GET /api/v1/invoices/{id},
including the paymentMethods array (the card + manual-method config; null means legacy dynamic
resolution). Keys are named after the resource:
| Event type | data keys |
|---|---|
invoice.sent | invoice |
invoice.paid | invoice, payment |
invoice.payment_failed | invoice, payment |
time_entry.created | time_entry |
task.completed | task |
task.created | task |
client.created | client |
project.created | project |
For the full field-by-field shape of each resource, see the API reference and the relevant resource docs: Invoices, Payments, Tasks, Clients, and Projects.
Payment events
invoice.paid and invoice.payment_failed include an additional payment object in data
alongside the invoice:
{
"id": "4f9c2a1e-....",
"type": "invoice.paid",
"created": 1717603200,
"orgId": "00000000-0000-0000-0000-000000000000",
"livemode": true,
"data": {
"invoice": { "/* full GET /api/v1/invoices/{id} shape */": true },
"payment": { "/* the payment that succeeded or failed */": true }
}
}Switch on the envelope's type to route events, then read the matching key out of data. Because
the id is stable across retries, record it before processing so repeated deliveries are no-ops.
Private payout events
The four team_bill.* and team_payment.* events require explicit Agency endpoint subscriptions;
existing selections do not expand automatically. Their payloads are minimal event snapshots,
not full GET bill/payment objects. They exclude compensation rates, bank/tax details, and
private attachment URLs. Keep your receiving system private and deduplicate by event ID.
See Payout integrations.
Test vs. live
The livemode flag tells you whether an event is real:
- Live events carry
livemode: true. - Test events (sent from the UI to a new endpoint) carry
livemode: falseand includedata.test: true. See Replay & test events.