Skip to content

Webhooks

Webhooks

Webhook endpoint management for receiving real-time event notifications. Configure URLs, select events, and monitor delivery status.

Event types

Subscribe to any combination of these events:

CategoryEvents
Invoicesinvoice.created invoice.updated invoice.sent invoice.paid invoice.overdue invoice.cancelled
Customerscustomer.created customer.updated customer.deleted
Paymentspayment.received payment.failed
Estimatesestimate.created estimate.sent estimate.accepted estimate.rejected
Credit notescredit_note.created credit_note.issued
Advance invoicesadvance_invoice.created advance_invoice.paid advance_invoice.applied

Payload format

Every webhook delivery sends a JSON body with this structure:

{
"event": "invoice.created",
"data": { ... },
"timestamp": "2024-01-15T10:30:00.000Z"
}

Delivery headers

Each delivery includes these headers:

HeaderDescription
X-Webhook-SignatureHMAC-SHA256 signature: sha256=<hex>
X-Webhook-TimestampUnix timestamp (seconds) when the payload was signed
X-Webhook-EventEvent type (e.g. invoice.created)
X-Webhook-DeliveryUnique delivery attempt ID
X-Webhook-IdWebhook endpoint ID
User-AgentSpaceInvoices-Webhook/1.0

Verifying signatures

Signatures use HMAC-SHA256. To verify:

  1. Get the raw request body (do not parse it first)
  2. Compute HMAC-SHA256(secret, body) using your webhook secret
  3. Compare the hex digest with the value after sha256= in X-Webhook-Signature
const crypto = require('crypto');
function verifySignature(secret, payload, signature) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return signature === `sha256=${expected}`;
}

Retry policy

Failed deliveries are retried up to 5 attempts with exponential backoff:

AttemptDelay
1Immediate
21 minute
35 minutes
430 minutes
52 hours

Webhooks are automatically disabled after all retry attempts are exhausted, and an email notification is sent to the entity owner.

HTTP status codes 400, 401, 403, 404, 405, and 410 are treated as permanent failures and are not retried.

The Webhook object

object

Attributes

idstring
urlstring
eventsarray of strings
descriptionstring
activeboolean
secretstring

Masked secret (only shown on create and rotate)

metadataobject

Default: {}

Other attributes
entity_idstring
created_atstring<date-time>
updated_atstring<date-time>
Examplejson
{
  "id": "whk_6595a27b5d35015c3ef0c3fd",
  "entity_id": "ent_6595a27b5d35015c3ef0c3fd",
  "url": "https://example.com/webhooks",
  "description": null,
  "secret": "whsec_****...****",
  "events": [
    "invoice.created",
    "invoice.paid"
  ],
  "active": true,
  "metadata": {},
  "created_at": "2024-01-01T00:00:00.000Z",
  "updated_at": "2024-01-01T00:00:00.000Z"
}

Create a new webhook

POST/webhooks

Create a webhook endpoint. The response includes the full secret which is only visible on creation and when rotated.

Header parameters

entity_idstringoptional

Entity ID on which the request is made. Auto-selected when only one entity exists, required when multiple entities exist.

Body parameters

urlstring<uri>required

The HTTPS URL to send webhook events to. Cannot target localhost, private IPs, or internal domains.

eventsarray of stringsrequired

Events that trigger this webhook

descriptionstringoptional

Optional description for this webhook

activebooleanoptional

Whether webhook is active

Default: true

metadataobjectoptional

Custom key-value data for your own use. Store any JSON object up to 50 properties. Values must be strings up to 250 characters. Useful for storing external IDs, tags, or integration data.

curl -X POST "https://eu.spaceinvoices.com/webhooks" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "x-entity-id: YOUR_ENTITY_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/webhooks",
    "events": [
      "invoice.created"
    ]
  }'
Example:

Returns

idstring
urlstring
eventsarray of strings
descriptionstring
activeboolean
secretstring
metadataobject

Default: {}

Other parameters
entity_idstring
created_atstring<date-time>
updated_atstring<date-time>
json
{
  "id": "whk_6595a27b5d35015c3ef0c3fd",
  "entity_id": "ent_6595a27b5d35015c3ef0c3fd",
  "url": "https://example.com/webhooks",
  "description": null,
  "secret": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2",
  "events": [
    "invoice.created",
    "invoice.paid"
  ],
  "active": true,
  "metadata": {},
  "created_at": "2024-01-01T00:00:00.000Z",
  "updated_at": "2024-01-01T00:00:00.000Z"
}

List all webhooks

GET/webhooks

Retrieve a paginated list of webhooks for the entity. Secrets are masked in the response.

Header parameters

entity_idstringoptional

Entity ID on which the request is made. Auto-selected when only one entity exists, required when multiple entities exist.

Query parameters

limitintegeroptional

Number of results per request.

Default: 10

next_cursorstringoptional

Cursor to fetch the next page of results. Use the value from pagination.next_cursor in the previous response.

prev_cursorstringoptional

Cursor to fetch the previous page of results. Use the value from pagination.prev_cursor in the previous response.

include_total_countbooleanoptional

Whether to include the total count of items in pagination.total.
Default is true.
When false, pagination.total returns -1 for better performance.

curl "https://eu.spaceinvoices.com/webhooks" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "x-entity-id: YOUR_ENTITY_ID"

Returns

dataarray of objects
paginationobject

Pagination metadata including cursors and result counts

json
{
  "data": [
    {
      "id": "whk_6595a27b5d35015c3ef0c3fd",
      "entity_id": "ent_6595a27b5d35015c3ef0c3fd",
      "url": "https://example.com/webhooks",
      "description": null,
      "secret": "whsec_****...****",
      "events": [
        "invoice.created",
        "invoice.paid"
      ],
      "active": true,
      "metadata": {},
      "created_at": "2024-01-01T00:00:00.000Z",
      "updated_at": "2024-01-01T00:00:00.000Z"
    }
  ],
  "pagination": {
    "total": 1,
    "next_cursor": null,
    "prev_cursor": null,
    "has_more": false
  }
}

Get a webhook by ID

GET/webhooks/{id}

Retrieve a single webhook. The secret is masked in the response.

Header parameters

entity_idstringoptional

Entity ID on which the request is made. Auto-selected when only one entity exists, required when multiple entities exist.

Path parameters

idstring<resource-id>required

Unique resource identifier

curl "https://eu.spaceinvoices.com/webhooks/{id}" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "x-entity-id: YOUR_ENTITY_ID"

Returns

idstring
urlstring
eventsarray of strings
descriptionstring
activeboolean
secretstring

Masked secret (only shown on create and rotate)

metadataobject

Default: {}

Other parameters
entity_idstring
created_atstring<date-time>
updated_atstring<date-time>
json
{
  "id": "whk_6595a27b5d35015c3ef0c3fd",
  "entity_id": "ent_6595a27b5d35015c3ef0c3fd",
  "url": "https://example.com/webhooks",
  "description": null,
  "secret": "whsec_****...****",
  "events": [
    "invoice.created",
    "invoice.paid"
  ],
  "active": true,
  "metadata": {},
  "created_at": "2024-01-01T00:00:00.000Z",
  "updated_at": "2024-01-01T00:00:00.000Z"
}

Update a webhook

PATCH/webhooks/{id}

Update webhook URL, events, active status, or description.

Header parameters

entity_idstringoptional

Entity ID on which the request is made. Auto-selected when only one entity exists, required when multiple entities exist.

Path parameters

idstring<resource-id>required

Unique resource identifier

Body parameters

urlstring<uri>optional

The HTTPS URL to send webhook events to. Cannot target localhost, private IPs, or internal domains.

eventsarray of stringsoptional
descriptionstringoptional
activebooleanoptional
metadataobjectoptional

Custom key-value data for your own use. Store any JSON object up to 50 properties. Values must be strings up to 250 characters. Useful for storing external IDs, tags, or integration data.

curl -X PATCH "https://eu.spaceinvoices.com/webhooks/{id}" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "x-entity-id: YOUR_ENTITY_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://new-url.example.com/webhooks"
  }'
Example:

Returns

idstring
urlstring
eventsarray of strings
descriptionstring
activeboolean
secretstring

Masked secret (only shown on create and rotate)

metadataobject

Default: {}

Other parameters
entity_idstring
created_atstring<date-time>
updated_atstring<date-time>
json
{
  "id": "whk_6595a27b5d35015c3ef0c3fd",
  "entity_id": "ent_6595a27b5d35015c3ef0c3fd",
  "url": "https://example.com/webhooks",
  "description": null,
  "secret": "whsec_****...****",
  "events": [
    "invoice.created",
    "invoice.paid"
  ],
  "active": true,
  "metadata": {},
  "created_at": "2024-01-01T00:00:00.000Z",
  "updated_at": "2024-01-01T00:00:00.000Z"
}

Delete a webhook

DELETE/webhooks/{id}

Permanently delete a webhook endpoint. Pending deliveries will be cancelled.

Header parameters

entity_idstringoptional

Entity ID on which the request is made. Auto-selected when only one entity exists, required when multiple entities exist.

Path parameters

idstring<resource-id>required

Unique resource identifier

curl -X DELETE "https://eu.spaceinvoices.com/webhooks/{id}" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "x-entity-id: YOUR_ENTITY_ID"

Returns

successboolean
json
{
  "success": true
}

Test a webhook

POST/webhooks/{id}/test

Send a test event to the webhook endpoint to verify connectivity.

Header parameters

entity_idstringoptional

Entity ID on which the request is made. Auto-selected when only one entity exists, required when multiple entities exist.

Path parameters

idstring<resource-id>required

Unique resource identifier

curl -X POST "https://eu.spaceinvoices.com/webhooks/{id}/test" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "x-entity-id: YOUR_ENTITY_ID"

Returns

idstring
Other parameters
webhook_idstring
entity_idstring
event_typestring
statusstring
request_bodyobject
response_statusinteger
response_bodystring
error_messagestring
attemptinteger
max_attemptsinteger
next_retry_atstring<date-time>
duration_msinteger
created_atstring<date-time>
completed_atstring<date-time>
json
{
  "id": "whd_6595a27b5d35015c3ef0c3fd",
  "webhook_id": "whk_6595a27b5d35015c3ef0c3fd",
  "entity_id": "ent_6595a27b5d35015c3ef0c3fd",
  "event_type": "webhook.test",
  "status": "success",
  "request_body": {
    "event": "webhook.test",
    "data": {}
  },
  "response_status": 200,
  "response_body": "OK",
  "error_message": null,
  "attempt": 1,
  "max_attempts": 1,
  "next_retry_at": null,
  "duration_ms": 150,
  "created_at": "2024-01-01T00:00:00.000Z",
  "completed_at": "2024-01-01T00:00:00.150Z"
}

List webhook deliveries

GET/webhooks/{id}/deliveries

Retrieve a paginated list of delivery attempts for a specific webhook.

Header parameters

entity_idstringoptional

Entity ID on which the request is made. Auto-selected when only one entity exists, required when multiple entities exist.

Path parameters

idstring<resource-id>required

Unique resource identifier

Query parameters

limitintegeroptional

Number of results per request.

Default: 10

next_cursorstringoptional

Cursor to fetch the next page of results. Use the value from pagination.next_cursor in the previous response.

prev_cursorstringoptional

Cursor to fetch the previous page of results. Use the value from pagination.prev_cursor in the previous response.

include_total_countbooleanoptional

Whether to include the total count of items in pagination.total.
Default is true.
When false, pagination.total returns -1 for better performance.

curl "https://eu.spaceinvoices.com/webhooks/{id}/deliveries" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "x-entity-id: YOUR_ENTITY_ID"

Returns

dataarray of objects
paginationobject

Pagination metadata including cursors and result counts

json
{
  "data": [
    {
      "id": "whd_6595a27b5d35015c3ef0c3fd",
      "webhook_id": "whk_6595a27b5d35015c3ef0c3fd",
      "entity_id": "ent_6595a27b5d35015c3ef0c3fd",
      "event_type": "invoice.created",
      "status": "success",
      "request_body": {
        "event": "invoice.created",
        "data": {
          "id": "inv_123"
        }
      },
      "response_status": 200,
      "response_body": "OK",
      "error_message": null,
      "attempt": 1,
      "max_attempts": 5,
      "next_retry_at": null,
      "duration_ms": 120,
      "created_at": "2024-01-01T00:00:00.000Z",
      "completed_at": "2024-01-01T00:00:00.120Z"
    }
  ],
  "pagination": {
    "total": 1,
    "next_cursor": null,
    "prev_cursor": null,
    "has_more": false
  }
}

Rotate webhook secret

POST/webhooks/{id}/rotate-secret

Generate a new signing secret for the webhook. The previous secret is immediately invalidated.

Header parameters

entity_idstringoptional

Entity ID on which the request is made. Auto-selected when only one entity exists, required when multiple entities exist.

Path parameters

idstring<resource-id>required

Unique resource identifier

curl -X POST "https://eu.spaceinvoices.com/webhooks/{id}/rotate-secret" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "x-entity-id: YOUR_ENTITY_ID"

Returns

idstring
urlstring
eventsarray of strings
descriptionstring
activeboolean
secretstring
metadataobject

Default: {}

Other parameters
entity_idstring
created_atstring<date-time>
updated_atstring<date-time>
json
{
  "id": "whk_6595a27b5d35015c3ef0c3fd",
  "entity_id": "ent_6595a27b5d35015c3ef0c3fd",
  "url": "https://example.com/webhooks",
  "description": null,
  "secret": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2",
  "events": [
    "invoice.created",
    "invoice.paid"
  ],
  "active": true,
  "metadata": {},
  "created_at": "2024-01-01T00:00:00.000Z",
  "updated_at": "2024-01-01T00:00:00.000Z"
}