<!-- source: https://secondlayer.tools/docs/api-reference#create-webhook -->

## Create a webhook

`POST /api/webhooks` (self-hosted instances only)

Takes `triggers` and starts at the chain tip, with no backfill. The response carries the signing secret. This is the only time it is returned, so store it now; `rotate-secret` issues a new one.

### Body

| Field | Type | Description |
| --- | --- | --- |
| `name` | string, required | Unique name on this instance. |
| `url` | string, required | Delivery target, `http://` or `https://`. Private addresses are refused at delivery time. |
| `triggers` | array of ChainTrigger, required | 1 to 50 triggers. An event matching any of them is delivered. |
| `format` | enum | Payload and signing format. `standard-webhooks` signs with the webhook's secret; the others shape the body for that receiver. |
| `authConfig` | object | Per-format receiver credentials: `token` / `tokenEnc` for `trigger` and `cloudflare`, `headers` or `basicAuth` for `raw`. Write-only — never returned; a read gets `auth` instead. |
| `maxRetries` | integer · 0 to 100 | Attempts before an event moves to the dead-letter queue. Default 7. |
| `timeoutMs` | integer · 100 to 300000 | Per-attempt HTTP timeout in milliseconds. Default 10000. |
| `concurrency` | integer · 1 to 100 | Deliveries in flight at once for this webhook. Default 4. |

### Returns

One `WebhookWithSecret` object.

### Responses

- `201` Created. The body carries the signing secret, once
- `400` A parameter, filter, cursor or body field was refused; `code` says which. A body carrying `subgraphName`, `tableName` or `filter` is answered with `SUBGRAPH_WEBHOOKS_REMOVED`: subgraph webhooks were removed
- `401` This bind requires the instance token, and it was missing or wrong
- `409` A webhook with that name already exists
- `415` Missing or non-JSON `Content-Type` (`UNSUPPORTED_MEDIA_TYPE`) — writes must send `Content-Type: application/json`
- `500` The webhook could not be stored (`INTERNAL_ERROR`)

### Example

```bash
curl -X POST http://127.0.0.1:3800/api/webhooks \
  -H "Authorization: Bearer $INSTANCE_TOKEN" \
  --json '{
  "name": "sbtc-moves",
  "url": "https://example.com/webhooks/sbtc",
  "triggers": [
    {
      "type": "ft_transfer",
      "assetIdentifier": "SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token::sbtc-token",
      "minAmount": "100000"
    },
    {
      "type": "print_event",
      "contractId": "SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-registry"
    }
  ]
}'
```

```json
{
  "webhook": {
    "id": "3f1c2a9e-8b7d-4c21-9a55-6e0d4b2f7c18",
    "name": "sbtc-moves",
    "status": "active",
    "kind": "chain",
    "format": "standard-webhooks",
    "url": "https://example.com/webhooks/sbtc",
    "lastDeliveryAt": "2026-09-22T14:03:11.402Z",
    "lastSuccessAt": "2026-09-22T14:03:11.402Z",
    "circuitOpenedAt": null,
    "circuitFailures": 0,
    "managedBy": null,
    "createdAt": "2026-09-20T09:12:45.118Z",
    "updatedAt": "2026-09-20T09:12:45.118Z",
    "triggers": [
      {
        "type": "ft_transfer",
        "assetIdentifier": "SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token::sbtc-token",
        "minAmount": "100000"
      },
      {
        "type": "print_event",
        "contractId": "SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-registry"
      }
    ],
    "auth": {
      "type": "none",
      "headerNames": [],
      "hasSecret": false
    },
    "maxRetries": 7,
    "timeoutMs": 10000,
    "concurrency": 4,
    "lastError": null,
    "receiverCertExpiresAt": "2026-12-25T22:56:35.000Z",
    "warning": null
  },
  "signingSecret": "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
}
```
