Sylphx EventsProduct site

First success

From an event to a receipt.

This is the shortest contract-derived path. It does not create a second API, a project header, or a success-shaped fallback. Live /v1 admission still requires an active Binding and Identity delegation on the API.

1. Use the API, not this site

Discover api_base from the Binding. The compiled default is https://api.events.sylphx.com/v1. Events does not mint Binding.

export SYLPHX_PROJECT_BINDING='<sylphx-project-binding+jwt>'
export SYLPHX_DELEGATION='<short-lived-identity-delegation>'
BINDING="x-sylphx-project-binding: $SYLPHX_PROJECT_BINDING"
AUTH="Authorization: Bearer $SYLPHX_DELEGATION"
BASE_URL="https://api.events.sylphx.com/v1"

Do not post events to https://events.sylphx.com. This host is the product site. Health probes live outside /v1 and are not first success.

2. Admit one immutable event

Every mutation has a caller-owned idempotency key. Same key, same digest: replay. Same key, different digest: conflict.

curl --fail-with-body -X POST "$BASE_URL/events" \
  -H "$BINDING" -H "$AUTH" -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: example-event-01' \
  --data '{
    "specversion": "1.0",
    "id": "order-123.created",
    "source": "https://shop.example.test/orders",
    "type": "com.example.order.created",
    "subject": "order-123",
    "dataContentType": "application/json",
    "data": {"orderId": "order-123"}
  }'

Keep event.eventId. The response is admission, not delivery.

3. Bind a webhook connector

Secrets are write-only and never return on readback. Endpoints are HTTPS only.

curl --fail-with-body -X POST "$BASE_URL/connectors" \
  -H "$BINDING" -H "$AUTH" -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: example-connector-01' \
  --data '{
    "name": "orders-webhook",
    "webhook": {
      "endpointUrl": "https://hooks.example.test/events",
      "signingSecretRef": "secret:orders-webhook-signing"
    }
  }'

Keep connector.connectorId.

4. Submit a delivery

curl --fail-with-body -X POST "$BASE_URL/deliveries" \
  -H "$BINDING" -H "$AUTH" -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: example-delivery-01' \
  --data '{
    "intent": {"webhook": {"connectorId": "<connector-id>", "eventId": "<event-id>"}},
    "retryPolicy": {
      "maximumAttempts": 3,
      "initialBackoffSeconds": 5,
      "maximumBackoffSeconds": 300,
      "perAttemptTimeoutSeconds": 30,
      "expiresAt": "<future-rfc3339>",
      "multiplier": 2,
      "maximumJitterSeconds": 5
    }
  }'

delivery.deliveryId is the durable operation identity. HTTP 202 is admission, not provider acceptance.

5. Read attempts and the receipt

First success is exact readback: GET /v1/deliveries/{id}, then attempts and receipts under the same project. A provider acceptance id is evidence from that provider. Transport ambiguity remains immutable evidence. Do not “fix” it with a manual resend of the same delivery.

curl --fail-with-body "$BASE_URL/deliveries/<delivery-id>" -H "$BINDING" -H "$AUTH"
curl --fail-with-body "$BASE_URL/deliveries/<delivery-id>/attempts?limit=20" -H "$BINDING" -H "$AUTH"
curl --fail-with-body "$BASE_URL/deliveries/<delivery-id>/receipts?limit=20" -H "$BINDING" -H "$AUTH"

If a request is unavailable, retry is an Events-owned state-machine decision. A new customer attempt after a terminal result is a new delivery with a new idempotency key.