SolidHooksSolidHooks/ Docs

Destinations

Define where SolidHooks delivers events, how each request is shaped and signed, and what happens when an endpoint stops answering.

A destination is an outbound HTTP endpoint that receives your events: your own server, a queue consumer, another SaaS. It holds the URL, the method, any headers you want sent, an optional delivery rate limit and a signing secret. It does not decide which events arrive. Connections do that.

Destination IDs start with dest_. Every destination lives in one project.

Types

typeURLWhen to use it
httpA publicly routable URL.The endpoint is on the internet.
privateAn in-network URL, plus a connectorId.The endpoint sits inside a private network or cluster and is reached through a connector.

connectorId is required for private and rejected for http. For private destinations the url is still validated as a URL, but it only has to resolve inside your network. See Private delivery for connectors.

For an http destination, SolidHooks refuses to store a URL that resolves to a private or reserved address.

Fields

FieldNotes
nameRequired, up to 100 characters.
descriptionOptional, up to 500 characters.
typehttp or private.
connectorIdOnly for private.
urlMust be a valid URL.
methodPOST, PUT or PATCH.
headersArray of { "value": "Name: Value", "sensitive": boolean }.
forwardHeadersWhen true, the inbound source request headers are forwarded, each renamed x-forwarded-src-<name>. Sensitive headers such as Authorization, Cookie and API keys are always excluded.
timeoutMs1000 to 30000. Defaults to 20000 (20 seconds) when unset.
rateLimitA whole number of 1 or more.
rateLimitPeriodsecond, minute, hour or day.

rateLimit and rateLimitPeriod must be set together or both left empty. Send null for both to remove a limit. See Rate limits for how this layers with your plan's delivery throughput.

Headers

Each header is a single string in Name: Value form. Mark a header sensitive and it is encrypted at rest and never returned by the API. Non-sensitive headers are returned as written.

On update, resubmitting a sensitive header with an empty value keeps the stored value, so a form that never saw the secret does not wipe it.

Besides your headers, every delivery carries these platform headers: x-source-id, x-event-id, x-organization-id, x-destination-id and x-delivery-id.

Create a destination

curl -s "$API/v1/destinations" \
  -H "authorization: Bearer $SDHK_KEY" \
  -H 'content-type: application/json' \
  -d '{
    "name": "My app",
    "type": "http",
    "url": "https://myapp.example.com/webhooks",
    "method": "POST",
    "headers": [{ "value": "x-internal-token: supersecret", "sensitive": true }],
    "rateLimit": 10,
    "rateLimitPeriod": "second",
    "timeoutMs": 15000
  }'

The response includes the destination id. Update with PATCH /v1/destinations/{id}, putting only the fields that change inside data: { "data": { "timeoutMs": 10000 } }.

Signing at a glance

Every destination gets a signing secret (whsec_...) when it is created, and every delivery it receives is signed. The signatureScheme field tells you how:

  • standard_webhooks is the default for new destinations. Deliveries carry webhook-id, webhook-timestamp and webhook-signature.
  • solidhooks_v1 is the older scheme, kept for destinations created before Standard Webhooks became the default.

Reveal the secret with GET /v1/destinations/{id}/signing-secret and rotate it with POST /v1/destinations/{id}/rotate-signing-secret. Both need a key scoped destination: read & write. How to verify a signature, rotation overlap and switching scheme are covered in Delivery signatures.

Send a test delivery

POST /v1/destinations/{id}/test-delivery posts a signed sample payload to the destination and reports what came back. It is shaped like a real delivery (same method, headers, signing and timeout) but carries x-solidhooks-test: true, records no event or delivery, and is never billed.

curl -s -X POST "$API/v1/destinations/dest_.../test-delivery" \
  -H "authorization: Bearer $SDHK_KEY"
{
  "delivered": true,
  "status": 200,
  "responseTimeMs": 84,
  "responseBody": "ok",
  "error": null,
  "signed": true
}

delivered means the receiver answered at all, not that it accepted the request. A connection failure comes back in error with a 200 response from SolidHooks, not as an API error.

In the dashboard, open the destination and use Send test under Send a test delivery.

DESTINATION_NOT_PUBLIC

If an http destination's URL resolves into a private or reserved range (loopback, RFC1918, CGNAT, link-local including cloud metadata), the test is refused with 400 DESTINATION_NOT_PUBLIC. SolidHooks will not dial those addresses on your behalf. Point the destination at a publicly routable address, or make it a private destination routed through a connector.

A disabled destination also refuses a test with 400. Enable it first.

410 Gone disables a destination

A destination that answers 410 Gone is treated as retired. Two consecutive 410 responses disable the destination and email the organisation's owners and admins. Any other response in between resets the count to zero, so a 410, 500, 410 sequence is a flapping endpoint, not a retired one.

Editing a destination resets the count when the change affects what is sent or where: url, method, headers, forwardHeaders, type, connectorId, or the signing secret or scheme. A 410 that was already in flight against the old configuration is discarded rather than counted against the new one. Changing only rateLimit, rateLimitPeriod, timeoutMs or the disabled flag does not reset it.

Enabling a destination does not clear the count. If you re-enable one without fixing the cause, the next 410 can disable it again immediately.

How other status codes are handled is on Retries.

Disable and enable

A disabled destination stops receiving deliveries. Its connections stay in place, so enabling it resumes delivery without reconfiguring anything. Deliveries that reach a disabled destination are recorded as skipped, and new events no longer match connections that point at it.

curl -s -X POST "$API/v1/destinations/dest_.../toggle-disabled" \
  -H "authorization: Bearer $SDHK_KEY" \
  -H 'content-type: application/json' \
  -d '{ "isDisabled": true }'

Send "isDisabled": false to enable it again.

Delivery health

GET /v1/destinations/{id}/delivery-health returns delivery outcome counts for the destination since an instant (since, default the last 24 hours): total, succeeded, failed, pending, skipped and since. Counts are anchored on when each delivery was created.

Delete a destination

DELETE /v1/destinations/{id} fails with 400 while any connection still references the destination. Delete its connections first. Deleting needs a key scoped destination: read & write; an unscoped key cannot delete.

In the dashboard, use Delete on the destination page and confirm.

Next steps

API reference: Destinations.