Skip to main content
SignSealShip pushes signed events to your HTTPS endpoint the moment things happen: a document is sealed, an order joins a room, a Closing Passport is minted. Every delivery carries an HMAC signature you must verify before trusting the payload.

Topics

Subscribe

There are two ways to register an endpoint. Both require an https:// URL, and both return the signing secret exactly once — SignSealShip stores only its SHA-256 hash, so a breach of our database never yields a key that can forge deliveries. Partner dashboard (all topics). Sign in at signsealship.com/partner and add a webhook under webhook management, choosing the topics you want. The response shows your sss_whsec_ secret once; copy it into your secret manager immediately. Proof Passport API (passport.sealed only). Registering with your partner key subscribes you to sealed-passport events:
Register a passport webhook
Response
See the webhooks API reference for the full endpoint shapes.

Event payloads

Deliveries are JSON POSTs. The event name also rides in a SignSealShip-Event header, and the id field is deterministic per event so you can deduplicate redeliveries.
passport.sealed
room.order_attached
room.passport_sealed
Room events carry only the field relevant to them: orderCode on order-attached, passportVerifyCode on passport-sealed. Payloads are PII-free and never contain another partner’s data.

The signature scheme

Every delivery includes:
Headers
The scheme mirrors the widely used t=,v1= format, so existing verification code ports directly:
  1. Derive the key once. The HMAC key is the UTF-8 bytes of lowercase_hex(sha256(your_raw_secret)). You re-derive it from the sss_whsec_ secret you were shown; SignSealShip signs with the stored hash, so the two sides always agree without the secret ever being stored in reversible form.
  2. Build the signed payload. Concatenate the t value, a literal ., and the raw request body bytes: {t}.{rawBody}.
  3. Compute HMAC-SHA256 over that payload with the derived key, hex-encoded lowercase.
  4. Compare against v1 with a constant-time comparison.
  5. Reject stale timestamps. Refuse deliveries where t is more than 5 minutes from now — this bounds replay of a captured request.
Verify against the raw body bytes exactly as received. Parsing the JSON and re-serializing it will change the bytes and the signature will not match.

Complete verification recipe

Both examples read the raw body, verify the signature and timestamp, and only then parse the event.
Test your endpoint before going live: compute a signature locally with your secret and a sample payload, POST it to yourself, and confirm your verifier accepts it — then flip one byte of the body and confirm it rejects.

Delivery semantics

  • Best-effort and non-blocking. Deliveries never block or fail the operation that triggered them. A sealed passport is durable before its webhook fires.
  • Respond fast with a 2xx. Acknowledge immediately and process asynchronously; slow endpoints get timed out at 10 seconds.
  • Expect redeliveries. Deliveries can be enqueued and retried, so design your handler to be idempotent. Deduplicate on the id field — it is deterministic per event.
  • Ordering is not guaranteed. Use occurredAtUtc / sealedAtUtc when sequence matters.
  • Verify every delivery. An unsigned or badly signed request to your endpoint is not from SignSealShip. Return a 400 and ignore it.

Webhooks API reference

Endpoint shapes for registering, listing, and deleting webhook subscriptions.

Closing Passport

What the room.passport_sealed event points at, and how to verify it.