> For a complete page index of the Captain API documentation, fetch https://docs.filesearch.dev/llms.txt?excludeSpec=true

# Subscribe Event Webhook

POST https://api.filesearch.dev/v2/syncs/{sync_id}/webhooks
Content-Type: application/json

Enroll a connector for real-time change events. For AWS S3, pass your SNS topic's ARN: Captain binds it to this sync and returns the queue_arn of Captain's SQS queue to subscribe the topic to, so events flow AWS-natively with no HTTP endpoint or signature handling on your side. For GCS, R2, Supabase, Backblaze, and Azure Blob, send an empty body: Captain mints a webhook secret and returns a private ingest_url to point the store's change notifications at, plus provider-specific setup steps. Pass rotate_secret to revoke a leaked ingest_url and mint a fresh one. Events complement the scheduled reconcile, which remains the reliability backstop. Idempotent per connector.

Reference: https://docs.filesearch.dev/reference/sync/manage/webhooks

## Request

### Path parameters

- `sync_id` (string, required) — The sync id.

### Headers

- `authorization` (string, optional, nullable) — Bearer \{api\_key}. Your Captain API key.

### Body (application/json)

This endpoint expects a S3SyncSubscribeRequestDocs.

- `sns_topic_arn` (string, optional, nullable) — ARN of your SNS topic that S3 event notifications are published to. Bound to the connector so events from this topic resolve here. AWS S3 only.
- `rotate_secret` (boolean, optional, default: false) — Webhook providers (GCS/R2/Supabase/Backblaze/Azure) only: mint a FRESH webhook secret, revoking the current ingest_url. Use if the URL may have leaked. The response returns the new ingest_url; the old one stops working immediately. Re-point your provider's webhook at the new URL.

## Response

### 200

Successful Response

- `sync_id` (string, optional)
- `queue_arn` (string, optional, nullable) — Captain's SQS queue ARN to subscribe your SNS topic to (AWS S3 real-time events). Subscribe your topic to this ARN; Captain consumes it directly (AWS-native, no HTTP endpoint). Null for webhook providers.
- `topic_arn_bound` (string, optional, nullable) — The SNS topic ARN bound to this connector (echoed back when provided). Null for webhook providers.
- `ingest_url` (string, optional, nullable) — Webhook-provider ingest URL (GCS push / R2 / Supabase / Backblaze): point the provider's change notifications at this URL. The URL embeds this connector's secret; keep it private. Null for AWS S3.
- `secret_set` (boolean, optional)
- `instructions` (list of string, optional) — Provider-native setup steps for wiring real-time change events (S3: SNS -> Captain's SQS queue; webhook providers: pointing notifications at the ingest_url).

## Examples

### AWS S3: bind an SNS topic

**Request**

```json
{
  "sns_topic_arn": "arn:aws:sns:us-east-1:123456789012:my-bucket-events"
}
```

**Response**

```json
{
  "sync_id": "sync_7e0abee6e110457db2c8",
  "queue_arn": "arn:aws:sqs:us-east-1:210987654321:captain-sync-events-prod",
  "topic_arn_bound": "arn:aws:sns:us-east-1:123456789012:my-bucket-events",
  "ingest_url": null,
  "secret_set": false,
  "instructions": [
    "In the AWS console for bucket 'my-bucket', open Properties -> Event notifications.",
    "Create a notification for 's3:ObjectCreated:*' and 's3:ObjectRemoved:*'.",
    "Set the destination to your SNS topic (arn:aws:sns:us-east-1:123456789012:my-bucket-events).",
    "Subscribe that SNS topic to Captain's SQS queue: arn:aws:sqs:us-east-1:210987654321:captain-sync-events-prod (SNS -> SQS; leave raw message delivery OFF so the SNS envelope and signature Captain relies on are preserved).",
    "Allow your topic to send to the queue if prompted. Captain consumes the queue directly; there is no endpoint to confirm.",
    "Reconciliation remains on as the backstop for any missed events."
  ]
}
```

**SDK Code**

```python Python REST
import requests

BASE_URL = "https://api.filesearch.dev"
API_KEY = "your_api_key"

headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json"
}

response = requests.post(
    f"{BASE_URL}/v2/syncs/sync_7e0abee6e110457db2c8/webhooks",
    headers=headers,
    json={"sns_topic_arn": "arn:aws:sns:us-east-1:123456789012:my-bucket-events"}
)

print(response.json()["queue_arn"])
```

```typescript TypeScript REST
const BASE_URL = "https://api.filesearch.dev";
const API_KEY = "your_api_key";

const response = await fetch(
    `${BASE_URL}/v2/syncs/{sync_id}/webhooks`,
    {
        method: "POST",
        headers: {
            "Authorization": `Bearer ${API_KEY}`,
            "Content-Type": "application/json"
        },
        body: JSON.stringify({
            sns_topic_arn: "arn:aws:sns:us-east-1:123456789012:my-bucket-events"
        })
    }
);

const data = await response.json();
console.log(data.queue_arn);
```

### GCS/R2/Supabase/Backblaze: get an ingest URL

**Response**

```json
{
  "sync_id": "sync_7e0abee6e110457db2c8",
  "queue_arn": null,
  "topic_arn_bound": null,
  "ingest_url": "https://tsl3s2h2wxrmuwcqhib5a4qkxe.lambda-url.us-east-1.on.aws/gcs/80PQJVil1beanR5bRB_BpZo1O1Yu6SRjAhKRBITD_Ao",
  "secret_set": true,
  "instructions": [
    "Create a Pub/Sub topic and a GCS bucket notification (OBJECT_FINALIZE + OBJECT_DELETE) for bucket 'my-bucket'.",
    "Create a PUSH subscription on that topic with delivery endpoint: https://tsl3s2h2wxrmuwcqhib5a4qkxe.lambda-url.us-east-1.on.aws/gcs/80PQJVil1beanR5bRB_BpZo1O1Yu6SRjAhKRBITD_Ao",
    "Keep this URL secret; it authenticates the event stream for this connector.",
    "Reconciliation remains on as the backstop for any missed events."
  ]
}
```

**SDK Code**

```python Python REST
import requests

BASE_URL = "https://api.filesearch.dev"
API_KEY = "your_api_key"

headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json"
}

response = requests.post(
    f"{BASE_URL}/v2/syncs/sync_7e0abee6e110457db2c8/webhooks",
    headers=headers
)

print(response.json()["ingest_url"])
```

```typescript TypeScript REST
const BASE_URL = "https://api.filesearch.dev";
const API_KEY = "your_api_key";

const response = await fetch(
    `${BASE_URL}/v2/syncs/{sync_id}/webhooks`,
    {
        method: "POST",
        headers: {
            "Authorization": `Bearer ${API_KEY}`,
            "Content-Type": "application/json"
        }
    }
);

const data = await response.json();
console.log(data.ingest_url);
```