> ## Documentation Index
> Fetch the complete documentation index at: https://docs.digitalseaservice.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks overview

> Signed, retried event delivery when a crew member's record changes.

When a crew member's record changes, we POST a signed event to every partner
who holds active consent and the relevant scope. You re-fetch via the API to
get the latest state. We retry on non-2xx with a documented schedule.

Webhooks are the recommended way to stay fresh — partners who poll instead
will hit conditional GETs (304 Not Modified) far more often than not and burn
rate-limit budget for no payload.

## What you receive

Every delivery looks like this:

```
POST https://yourapp.example.com/integrations/dss/webhook
Content-Type: application/json
X-DSS-Signature: t=1716714840,v1=99d56ccfe6de640971036fc31a8bb476415322e6b687301c96fe15ac81e3fcff
User-Agent: dss-public-api-webhooks/1

{"created_at":"2026-05-26T09:14:00Z","data":{"user_id":"65a1f0e2c3b4d5e6f7a8b9c0"},"id":"evt_3f4a9c8e2b1d4f5a8c9e0d1f2a3b4c5d","type":"user.sea_time.updated"}
```

The body is a **pointer**, not data. You call back via the API with the user's
access token to fetch the latest state. Two reasons:

1. Payloads stay small and our signing surface stays minimal.
2. Scope is re-evaluated at fetch time. If the user revoked between event
   firing and your fetch, the fetch fails cleanly (`401 invalid_token`)
   instead of leaking stale data.

The body is serialised with **sorted keys and no whitespace** so the
canonical form is stable across any intermediates that might re-serialise.

## Delivery model

```mermaid theme={null}
sequenceDiagram
  autonumber
  participant U as Crew member
  participant DSS as DSS dashboard
  participant API as DSS API
  participant Q as Cloud Tasks
  participant P as Your webhook URL

  U->>DSS: Updates profile / sea time / vessel
  DSS->>API: POST /internal/events/publish (event)
  API->>API: Fan out by consent + scope
  loop For each consenting partner
    API->>Q: Enqueue delivery
    Q->>P: POST signed event
    alt 2xx within 5s
      P-->>Q: 200 OK
    else non-2xx or timeout
      P--xQ: error
      Q->>Q: Schedule retry (1m → 5m → 30m → 2h → 6h)
    end
  end
```

### Reliability

* **At-least-once delivery.** You may see the same event twice — dedupe on
  `id`.
* **Retry schedule:** `+1m, +5m, +30m, +2h, +6h`. We give up after 24h
  (or 6 attempts total, whichever comes first).
* **Response budget:** 5 seconds. If your handler takes longer we treat it
  as a failure and retry.
* **Idempotency key** is the event `id`, stable across every attempt.

### Order

Webhooks are **not ordered**. Multiple events for the same user can arrive
out of order, especially after retries. Always fetch the latest state from
the API rather than trying to apply an event as a delta — the event tells you
"something changed," the API call tells you "this is the new value."

## Registering your endpoint

During onboarding you'll give us:

* A single HTTPS URL.
* A signing secret rotation contact (an email we ping when we plan to rotate).

We give you back a **webhook signing secret**, separate from your
`client_secret`. Store it next to your other secrets. The
`client_secret` and `webhook_secret` rotate independently — rotating either
one does not affect the other.

To change your webhook URL or rotate the secret in sandbox, email
[admin@digitalseaservice.com](mailto:admin@digitalseaservice.com).
Production rotation is handled by your DSS account contact.

## Testing your handler

Once your URL and secret are registered, call `GET /v1/webhooks/test` with a
valid access token and we'll fire a `webhook.test` event to your URL. It's
the same shape and signing as every other event — use it to validate
signature verification before any real user authorises you.

```bash theme={null}
curl -sS -H "Authorization: Bearer $ACCESS_TOKEN" \
  https://api.dev.digitalseaservice.com/v1/webhooks/test
```

A successful call returns `200 OK` with the `event_id`, `delivery_id`, and
the `target_url` we enqueued the delivery against:

```json theme={null}
{
  "event_id": "evt_3f4a9c8e2b1d4f5a8c9e0d1f2a3b4c5d",
  "delivery_id": "dlv_77f1bb31c0c64e6db4f5e8a9c2d3e4f5",
  "target_url": "https://yachtworkerscouncil.com/integrations/dss/webhook"
}
```

Watch your handler logs for the delivery.

## What's next

<CardGroup cols={2}>
  <Card title="Event catalogue" icon="list" href="/webhooks/events">
    Every event we send, with payload examples and recommended action.
  </Card>

  <Card title="Signature verification" icon="signature" href="/webhooks/verification">
    Drop-in code samples in Python and Node — copy these verbatim.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.