Skip to main content
Inbound webhooks let external services create records in a HASP app by POSTing signed JSON to a unique URL. The token in the URL addresses the webhook; the HMAC signature on each request authenticates it.
Inbound webhooks are the one place you call the Data API directly — no SDK is needed because the caller is an external service, not your app’s JavaScript.

Endpoint

The {token} is the full plaintext token shown in the HASP dashboard when you create the webhook. No X-Hasp-AppId header or session cookie is needed — the token identifies both the app and the target entity. The token is an address, not a credential: every request must also carry a valid signature (see Signing). Max body size: 256 KB.

Signing

Every request must be signed with the webhook’s signing secret, shown once in the dashboard when you create the webhook. Requests without a valid signature are rejected with 401 UNAUTHORIZED and no record is created. The scheme is HMAC-SHA256 over the timestamp and the raw request body:
  1. Take the current Unix time in seconds as timestamp.
  2. Build the signed string "{timestamp}.{raw_body}" — the timestamp, a literal ., then the exact bytes you send as the body.
  3. Compute the HMAC-SHA256 of that string keyed by the signing secret, hex-encoded.
  4. Send two headers:
If you configured a custom signature header on the webhook, send the signature in that header instead of X-Webhook-Signature. The timestamp header name does not change. The timestamp must be within ±300 seconds of HASP’s clock; anything older or further in the future is rejected, so a captured request cannot be replayed later. Because the body is part of the signed string, changing either the body or the timestamp invalidates the signature — sign the bytes exactly as you send them. Rotating the secret: after you rotate the signing secret, requests signed with the previous secret are still accepted for 7 days, so you can roll the new secret out to your sender without dropping requests. If the webhook has an IP allowlist, requests from any other address are rejected with 403 IP_NOT_ALLOWED before the signature is checked.

Idempotency

Include an X-Idempotency-Key header (max 255 characters) to prevent duplicate records from replayed or retried requests. HASP caches the response for 24 hours — repeat requests with the same key return the original response without creating a second record.

Request

The body must be a JSON object. Any other body (malformed or truncated JSON, a JSON array or scalar, form-encoded or plain text, or an empty body) is rejected with 422 INVALID_PAYLOAD, and no record is created. A rejected body does not consume its X-Idempotency-Key, so a corrected retry with the same key is processed normally. Keys are mapped to entity field keys according to the webhook’s field mapping (if configured). Unmapped keys are passed through directly. Keys that do not match any entity field are silently dropped.

Response

201 Created:

Error Responses

Loop Prevention

If the request contains an X-Hasp-Webhook-Id header (set automatically on all outbound webhook deliveries), the record.created event is not re-dispatched. This prevents outbound → inbound → outbound infinite loops.

Field Mapping

When creating an inbound webhook in the dashboard, you can configure a field mapping to translate incoming key names to your entity’s field keys. Unmapped keys are passed through as-is.