Skip to main content
API Reference

Triggers API

HTTP API for creating, managing, and inspecting CodeSpar triggers — outbound webhook subscriptions with signed deliveries, retries, and a dead-letter queue.

1 min read · updated

Triggers API

Base URL: https://api.codespar.dev

See the Triggers concept for the mental model (event catalog, retry semantics, signature verification). This page is the endpoint reference.

All endpoints require authentication via Bearer token. See Authentication.


POST /v1/triggers

Create a new trigger. The signing secret is returned once on this call and on rotate-secret — never in list/get responses.

Auth required: Yes (scope: triggers:write)

Request body

FieldTypeRequiredDescription
namestringYesFree-form label (shown in dashboard + delivery logs)
eventstringYesEvent filter (* or a specific event like payment.confirmed)
webhook_urlstringYesHTTPS endpoint. Must be reachable from api.codespar.dev
server_idstringNoRestrict to events from one MCP server

curl example

curl -X POST https://api.codespar.dev/v1/triggers \
  -H "Authorization: Bearer csk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "fulfillment-pipeline",
    "event": "payment.confirmed",
    "webhook_url": "https://yourapp.com/api/webhooks/codespar",
    "server_id": "asaas"
  }'

Response -- 201 Created

{
  "id": "trg_a1b2c3d4e5f6g7h8",
  "org_id": "org_xyz789",
  "name": "fulfillment-pipeline",
  "event": "payment.confirmed",
  "server_id": "asaas",
  "webhook_url": "https://yourapp.com/api/webhooks/codespar",
  "status": "active",
  "signing_enabled": true,
  "secret": "whsec_3k2j4h5g6f7d8s9a0p1o2i3u4y5t6r7e",
  "created_at": "2026-04-22T14:30:00Z"
}

The secret appears only on this response. Save it immediately to your secret manager (AWS Secrets Manager, Vercel Environment Variables, 1Password, etc.). If lost, use POST /v1/triggers/:id/rotate-secret to mint a new one — which invalidates the old.


GET /v1/triggers

List all triggers in the resolved project (via x-codespar-project header) or the org's default project.

Auth required: Yes (scope: triggers:read)

Query parameters

ParameterTypeDefaultDescription
statusstring--Filter by active, paused, or error
eventstring--Filter by exact event name
limitnumber50Results per page (max 100)
offsetnumber0Pagination offset

Response -- 200 OK

{
  "data": [
    {
      "id": "trg_a1b2c3d4e5f6g7h8",
      "name": "fulfillment-pipeline",
      "event": "payment.confirmed",
      "webhook_url": "https://yourapp.com/api/webhooks/codespar",
      "status": "active",
      "total_runs": 1847,
      "last_run_at": "2026-04-22T14:30:00Z",
      "created_at": "2026-04-01T09:00:00Z",
      "signing_enabled": true
    }
  ],
  "total": 1
}

secret is never included in list responses.


GET /v1/triggers/:id

Get a single trigger's current state.

Auth required: Yes (scope: triggers:read)

Response -- 200 OK

Same shape as a single entry in the list response. No secret.


PATCH /v1/triggers/:id

Update name, webhook URL, event filter, or status. Any field omitted is left unchanged.

Auth required: Yes (scope: triggers:write)

Request body

FieldTypeDescription
namestringNew label
eventstringNew event filter
webhook_urlstringNew HTTPS endpoint
statusstringactive or paused (error is read-only — set by the system)

Use case: resume an auto-paused trigger

curl -X PATCH https://api.codespar.dev/v1/triggers/trg_abc123 \
  -H "Authorization: Bearer csk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"status": "active"}'

DELETE /v1/triggers/:id

Hard-delete the trigger. In-flight deliveries are not cancelled — they run through retries and land in DLQ, but new events will not fire.

Auth required: Yes (scope: triggers:write)

Response -- 204 No Content


POST /v1/triggers/:id/rotate-secret

Mint a new signing secret. The old secret is invalidated immediately — plan a brief window where your endpoint accepts both (verify against the new secret, fall back to the old if rotation is in-flight, cut over once new traffic dominates).

Auth required: Yes (scope: triggers:write)

Response -- 200 OK

{
  "id": "trg_abc123",
  "secret": "whsec_NEW_secret_returned_once"
}

POST /v1/triggers/:id/test-fire

Push a synthetic event through the real delivery pipeline. Retries, signing, DLQ — everything operates exactly as it would in production. Use this to verify your endpoint ahead of a production release.

Auth required: Yes (scope: triggers:write)

Response -- 202 Accepted

{
  "delivery_id": "dlv_test_m3n4o5p6",
  "status": "queued",
  "fixture_event": "payment.confirmed"
}

GET /v1/triggers/:id/deliveries

List recent delivery attempts for a trigger — successes and failures.

Auth required: Yes (scope: triggers:read)

Query parameters

ParameterTypeDescription
statusstringFilter by succeeded, failed, pending, dead_lettered
fromstringISO 8601 timestamp
tostringISO 8601 timestamp
limitnumberDefault 50, max 100

Response -- 200 OK

{
  "data": [
    {
      "id": "dlv_m3n4o5p6",
      "trigger_id": "trg_abc123",
      "event": "payment.confirmed",
      "status": "succeeded",
      "attempt": 1,
      "http_status": 200,
      "duration_ms": 142,
      "fired_at": "2026-04-22T14:30:00Z"
    },
    {
      "id": "dlv_q7r8s9t0",
      "trigger_id": "trg_abc123",
      "event": "payment.confirmed",
      "status": "dead_lettered",
      "attempt": 12,
      "http_status": 504,
      "last_error": "Upstream timeout",
      "fired_at": "2026-04-22T10:15:00Z"
    }
  ],
  "total": 2
}

GET /v1/triggers/:id/deliveries/:did

Get a single delivery with the full request + response body — useful for DLQ debugging.

Auth required: Yes (scope: triggers:read)

Response -- 200 OK

{
  "id": "dlv_m3n4o5p6",
  "trigger_id": "trg_abc123",
  "event": "payment.confirmed",
  "status": "succeeded",
  "attempt": 1,
  "request": {
    "url": "https://yourapp.com/api/webhooks/codespar",
    "headers": {
      "X-CodeSpar-Signature": "7f9b4c...",
      "X-CodeSpar-Delivery-Id": "dlv_m3n4o5p6",
      "X-CodeSpar-Event": "payment.confirmed"
    },
    "body": "{\"type\":\"payment.confirmed\",\"data\":{...}}"
  },
  "response": {
    "status": 200,
    "body": "",
    "duration_ms": 142
  }
}

GET /v1/triggers/:id/dlq

Dead-lettered deliveries (exhausted 12 retries without a 2xx). Ordered by fired_at descending.

Auth required: Yes (scope: triggers:read)

Response shape matches /deliveries, filtered to status: "dead_lettered".


POST /v1/triggers/deliveries/:did/redeliver

Operator-driven single redelivery. Fires one fresh attempt through the normal pipeline.

Auth required: Yes (scope: triggers:write)

Response -- 202 Accepted

{
  "delivery_id": "dlv_m3n4o5p6",
  "status": "queued",
  "original_delivery_id": "dlv_m3n4o5p6",
  "redelivered_at": "2026-04-22T15:00:00Z"
}

Redeliveries reuse the original delivery ID + signature — your idempotency key check should still match.


POST /v1/triggers/retry-pending

Org-scoped operation: drain the retry queue immediately instead of waiting for the next scheduled retry. Useful after fixing a widespread outage.

Auth required: Yes (scope: triggers:write)

Response -- 200 OK

{
  "enqueued": 47,
  "triggers_affected": 3
}

Errors

Common status codes for this surface. See the full Error Reference for the complete list.

StatusError codeDescription
400invalid_requestMissing required field, bad event name, malformed URL
401unauthorizedInvalid or missing API key
403forbiddenAPI key lacks triggers:read / triggers:write scope
404not_foundTrigger ID does not exist in the resolved project
409webhook_url_conflictAnother active trigger in the same project already uses this URL + event pair
429rate_limitedToo many trigger-management calls; see Retry-After
500internal_errorRetry with exponential backoff

Next steps

Last updated on