# Sources

> Part of the Cowliss docs. Markdown for every page: append `index.md` to its URL. Site map: /llms.txt

<!-- GENERATED by apps/docs/scripts/generate-api-reference.ts. Do not edit. Regenerate with `pnpm -F cowliss-docs generate`. -->

A source is one inbound pipe into an [app](/reference/api/apps/): each app is created with one first-party `api` source and may hold several, each with a `name` unique among the app's active sources, while a `clerk` source is one per app. A deactivated source refuses everything naming it until it is activated again. Sources are created and listed under their parent app, and these routes address one by its own `id`. All writes are attributed to the source's parent app. The signing secret in `config` is write-only and never present in any response: `configured` reports whether one is stored. The webhook receiver is public: the caller's signature is the authorization, and the route answers with plain text rather than the envelope. See the [Apps and sources guide](/tracking/apps/).

## GET /v1/sources/{id}

Fetch one source

**Auth:** Session token.

**Response 200**

```json
{
  "data": {
    "id": "string",
    "orgId": "string",
    "appId": "string",
    "kind": "api",
    "name": "string",
    "config": "string",
    "lastReceivedAt": "2026-01-15T09:30:00.000Z",
    "createdAt": "2026-01-15T09:30:00.000Z",
    "updatedAt": "2026-01-15T09:30:00.000Z",
    "deactivatedAt": "2026-01-15T09:30:00.000Z",
    "status": "active",
    "configured": true
  },
  "meta": {
    "requestId": "string",
    "nextCursor": "string",
    "hasMore": true,
    "warnings": [
      {
        "code": "string",
        "message": "string",
        "suggestion": "string"
      }
    ]
  }
}
```

| Field | Type | Required | Constraints |
| --- | --- | --- | --- |
| `data` | object | yes |  |
| `data.id` | string | yes |  |
| `data.orgId` | string | yes |  |
| `data.appId` | string | yes |  |
| `data.kind` | "api" \| "clerk" | yes |  |
| `data.name` | string | yes |  |
| `data.config` | string \| number \| boolean \| null \| record\<string, any> \| any[] | yes |  |
| `data.lastReceivedAt` | string (date-time) \| null | yes |  |
| `data.createdAt` | string (date-time) | yes |  |
| `data.updatedAt` | string (date-time) | yes |  |
| `data.deactivatedAt` | string (date-time) \| null | yes |  |
| `data.status` | "active" \| "inactive" | yes |  |
| `data.configured` | boolean | yes |  |
| `meta` | object | no |  |
| `meta.requestId` | string | no |  |
| `meta.nextCursor` | string \| null | no |  |
| `meta.hasMore` | boolean | no |  |
| `meta.warnings` | object[] | no |  |
| `meta.warnings[]` | object | no |  |
| `meta.warnings[].code` | string | yes |  |
| `meta.warnings[].message` | string | yes |  |
| `meta.warnings[].suggestion` | string | no |  |

Errors follow the [shared envelope and code table](/api-conventions/#errors).

## PATCH /v1/sources/{id}

Rename a source, or set or rotate its config

Admin only. The name must be unique among the app's active sources. A `clerk` source is named after its kind, so it takes a config and no name.

**Auth:** Session token.

**Request body**

```json
{
  "data": {
    "name": "string",
    "config": {}
  }
}
```

| Field | Type | Required | Constraints |
| --- | --- | --- | --- |
| `data` | object | yes |  |
| `data.name` | string | no | length 1–100 |
| `data.config` | any | no |  |

**Response 200**

```json
{
  "data": {
    "id": "string",
    "orgId": "string",
    "appId": "string",
    "kind": "api",
    "name": "string",
    "config": "string",
    "lastReceivedAt": "2026-01-15T09:30:00.000Z",
    "createdAt": "2026-01-15T09:30:00.000Z",
    "updatedAt": "2026-01-15T09:30:00.000Z",
    "deactivatedAt": "2026-01-15T09:30:00.000Z",
    "status": "active",
    "configured": true
  },
  "meta": {
    "requestId": "string",
    "nextCursor": "string",
    "hasMore": true,
    "warnings": [
      {
        "code": "string",
        "message": "string",
        "suggestion": "string"
      }
    ]
  }
}
```

| Field | Type | Required | Constraints |
| --- | --- | --- | --- |
| `data` | object | yes |  |
| `data.id` | string | yes |  |
| `data.orgId` | string | yes |  |
| `data.appId` | string | yes |  |
| `data.kind` | "api" \| "clerk" | yes |  |
| `data.name` | string | yes |  |
| `data.config` | string \| number \| boolean \| null \| record\<string, any> \| any[] | yes |  |
| `data.lastReceivedAt` | string (date-time) \| null | yes |  |
| `data.createdAt` | string (date-time) | yes |  |
| `data.updatedAt` | string (date-time) | yes |  |
| `data.deactivatedAt` | string (date-time) \| null | yes |  |
| `data.status` | "active" \| "inactive" | yes |  |
| `data.configured` | boolean | yes |  |
| `meta` | object | no |  |
| `meta.requestId` | string | no |  |
| `meta.nextCursor` | string \| null | no |  |
| `meta.hasMore` | boolean | no |  |
| `meta.warnings` | object[] | no |  |
| `meta.warnings[]` | object | no |  |
| `meta.warnings[].code` | string | yes |  |
| `meta.warnings[].message` | string | yes |  |
| `meta.warnings[].suggestion` | string | no |  |

Errors follow the [shared envelope and code table](/api-conventions/#errors).

## POST /v1/sources/{id}/deactivate

Deactivate a source

Admin only. Calls and webhook deliveries naming this source are refused from now on, until it is activated again. Its id stays the same, and everything it already sent keeps pointing at it.

**Auth:** Session token.

**Response 200**

```json
{
  "data": {
    "id": "string",
    "orgId": "string",
    "appId": "string",
    "kind": "api",
    "name": "string",
    "config": "string",
    "lastReceivedAt": "2026-01-15T09:30:00.000Z",
    "createdAt": "2026-01-15T09:30:00.000Z",
    "updatedAt": "2026-01-15T09:30:00.000Z",
    "deactivatedAt": "2026-01-15T09:30:00.000Z",
    "status": "active",
    "configured": true
  },
  "meta": {
    "requestId": "string",
    "nextCursor": "string",
    "hasMore": true,
    "warnings": [
      {
        "code": "string",
        "message": "string",
        "suggestion": "string"
      }
    ]
  }
}
```

| Field | Type | Required | Constraints |
| --- | --- | --- | --- |
| `data` | object | yes |  |
| `data.id` | string | yes |  |
| `data.orgId` | string | yes |  |
| `data.appId` | string | yes |  |
| `data.kind` | "api" \| "clerk" | yes |  |
| `data.name` | string | yes |  |
| `data.config` | string \| number \| boolean \| null \| record\<string, any> \| any[] | yes |  |
| `data.lastReceivedAt` | string (date-time) \| null | yes |  |
| `data.createdAt` | string (date-time) | yes |  |
| `data.updatedAt` | string (date-time) | yes |  |
| `data.deactivatedAt` | string (date-time) \| null | yes |  |
| `data.status` | "active" \| "inactive" | yes |  |
| `data.configured` | boolean | yes |  |
| `meta` | object | no |  |
| `meta.requestId` | string | no |  |
| `meta.nextCursor` | string \| null | no |  |
| `meta.hasMore` | boolean | no |  |
| `meta.warnings` | object[] | no |  |
| `meta.warnings[]` | object | no |  |
| `meta.warnings[].code` | string | yes |  |
| `meta.warnings[].message` | string | yes |  |
| `meta.warnings[].suggestion` | string | no |  |

Errors follow the [shared envelope and code table](/api-conventions/#errors).

## POST /v1/sources/{id}/activate

Activate a source

Admin only. Calls and webhook deliveries naming this source are accepted again. Refused when another active source on the app holds its name, or, for a `clerk` source, when the app already has an active one.

**Auth:** Session token.

**Response 200**

```json
{
  "data": {
    "id": "string",
    "orgId": "string",
    "appId": "string",
    "kind": "api",
    "name": "string",
    "config": "string",
    "lastReceivedAt": "2026-01-15T09:30:00.000Z",
    "createdAt": "2026-01-15T09:30:00.000Z",
    "updatedAt": "2026-01-15T09:30:00.000Z",
    "deactivatedAt": "2026-01-15T09:30:00.000Z",
    "status": "active",
    "configured": true
  },
  "meta": {
    "requestId": "string",
    "nextCursor": "string",
    "hasMore": true,
    "warnings": [
      {
        "code": "string",
        "message": "string",
        "suggestion": "string"
      }
    ]
  }
}
```

| Field | Type | Required | Constraints |
| --- | --- | --- | --- |
| `data` | object | yes |  |
| `data.id` | string | yes |  |
| `data.orgId` | string | yes |  |
| `data.appId` | string | yes |  |
| `data.kind` | "api" \| "clerk" | yes |  |
| `data.name` | string | yes |  |
| `data.config` | string \| number \| boolean \| null \| record\<string, any> \| any[] | yes |  |
| `data.lastReceivedAt` | string (date-time) \| null | yes |  |
| `data.createdAt` | string (date-time) | yes |  |
| `data.updatedAt` | string (date-time) | yes |  |
| `data.deactivatedAt` | string (date-time) \| null | yes |  |
| `data.status` | "active" \| "inactive" | yes |  |
| `data.configured` | boolean | yes |  |
| `meta` | object | no |  |
| `meta.requestId` | string | no |  |
| `meta.nextCursor` | string \| null | no |  |
| `meta.hasMore` | boolean | no |  |
| `meta.warnings` | object[] | no |  |
| `meta.warnings[]` | object | no |  |
| `meta.warnings[].code` | string | yes |  |
| `meta.warnings[].message` | string | yes |  |
| `meta.warnings[].suggestion` | string | no |  |

Errors follow the [shared envelope and code table](/api-conventions/#errors).

## POST /v1/sources/{sourceId}/webhook

Source webhook receiver (public)

**Auth:** None: verifies the caller's own signature/token.

Errors follow the [shared envelope and code table](/api-conventions/#errors).
