> ## Documentation Index
> Fetch the complete documentation index at: https://docs.planasonix.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Orchestration API

> Programmatically trigger syncs, backfills, and manage jobs.

The **Orchestration API** is the v1 external surface for automation tools (Airflow, Dagster, CI/CD, custom schedulers). Use it to start syncs and backfills, poll job status, fetch results, and manage webhook subscriptions.

<Info>
  Authenticate every request with a [Bearer API key](/api-reference/authentication) in the `Authorization` header.
</Info>

## Base URL and versioning

```text theme={null}
https://api.planasonix.com
```

Orchestration routes live under `/api/v1`. Example: `POST https://api.planasonix.com/api/v1/connections/{connectionId}/sync`.

***

## Trigger a sync job

```http theme={null}
POST /api/v1/connections/{connectionId}/sync
```

Starts an incremental sync for the given connection. The response includes a job you can poll.

<ParamField path="connectionId" type="string" required>
  Connection identifier (for example `conn_01jq8k2m3n4p5q6r7s8t9u0v1`).
</ParamField>

**Request body (optional)**

```json theme={null}
{
  "pipeline_id": "pl_01hqxyz",
  "priority": "normal",
  "metadata": {
    "triggered_by": "airflow_dag_nightly_sales",
    "dag_run_id": "scheduled__2025-03-27T02:00:00+00:00"
  }
}
```

**Response `202 Accepted`**

```json theme={null}
{
  "data": {
    "job": {
      "id": "job_01jq8syncabc",
      "type": "connection_sync",
      "status": "queued",
      "connection_id": "conn_01jq8k2m3n4p5q6r7s8t9u0v1",
      "pipeline_id": "pl_01hqxyz",
      "created_at": "2025-03-27T14:22:10Z",
      "queued_at": "2025-03-27T14:22:10Z"
    }
  }
}
```

***

## Trigger a backfill

```http theme={null}
POST /api/v1/connections/{connectionId}/backfill
```

Schedules a historical load over a time window. Large windows may shard into multiple internal jobs; the top-level backfill record tracks overall progress.

**Request body**

```json theme={null}
{
  "start_at": "2025-01-01T00:00:00Z",
  "end_at": "2025-03-01T00:00:00Z",
  "tables": ["public.orders", "public.order_items"],
  "priority": "low",
  "metadata": {
    "reason": "post_incident_replay"
  }
}
```

**Response `202 Accepted`**

```json theme={null}
{
  "data": {
    "backfill": {
      "id": "bf_01jq8bfill01",
      "connection_id": "conn_01jq8k2m3n4p5q6r7s8t9u0v1",
      "status": "running",
      "start_at": "2025-01-01T00:00:00Z",
      "end_at": "2025-03-01T00:00:00Z",
      "tables": ["public.orders", "public.order_items"],
      "progress_percent": 0,
      "created_at": "2025-03-27T14:25:00Z"
    }
  }
}
```

***

## List all backfills

```http theme={null}
GET /api/v1/backfills
```

**Query parameters (typical)**

| Parameter       | Description                                             |
| --------------- | ------------------------------------------------------- |
| `connection_id` | Filter by connection                                    |
| `status`        | `queued`, `running`, `completed`, `failed`, `cancelled` |
| `limit`         | Page size (default 50, max 100)                         |
| `cursor`        | Opaque pagination cursor                                |

**Response `200 OK`**

```json theme={null}
{
  "data": [
    {
      "id": "bf_01jq8bfill01",
      "connection_id": "conn_01jq8k2m3n4p5q6r7s8t9u0v1",
      "status": "running",
      "start_at": "2025-01-01T00:00:00Z",
      "end_at": "2025-03-01T00:00:00Z",
      "progress_percent": 37,
      "created_at": "2025-03-27T14:25:00Z",
      "updated_at": "2025-03-27T14:28:44Z"
    }
  ],
  "meta": {
    "page": {
      "limit": 50,
      "cursor": "eyJiZiI6ImJmXzAxanE4YmZpbGwwMSJ9"
    }
  }
}
```

***

## Get backfill status

```http theme={null}
GET /api/v1/backfills/{backfillId}
```

**Response `200 OK`**

```json theme={null}
{
  "data": {
    "id": "bf_01jq8bfill01",
    "connection_id": "conn_01jq8k2m3n4p5q6r7s8t9u0v1",
    "status": "running",
    "start_at": "2025-01-01T00:00:00Z",
    "end_at": "2025-03-01T00:00:00Z",
    "tables": ["public.orders", "public.order_items"],
    "progress_percent": 37,
    "rows_processed": 1849200,
    "error": null,
    "job_ids": ["job_01jq8chunk01", "job_01jq8chunk02"],
    "created_at": "2025-03-27T14:25:00Z",
    "updated_at": "2025-03-27T14:28:44Z"
  }
}
```

***

## List jobs

```http theme={null}
GET /api/v1/jobs
```

**Query parameters (typical)**

| Parameter         | Description                                             |
| ----------------- | ------------------------------------------------------- |
| `connection_id`   | Filter by connection                                    |
| `pipeline_id`     | Filter by pipeline                                      |
| `type`            | `connection_sync`, `backfill`, `pipeline_run`, etc.     |
| `status`          | `queued`, `running`, `succeeded`, `failed`, `cancelled` |
| `since`, `until`  | ISO 8601 bounds on `created_at`                         |
| `limit`, `cursor` | Pagination                                              |

**Response `200 OK`**

```json theme={null}
{
  "data": [
    {
      "id": "job_01jq8syncabc",
      "type": "connection_sync",
      "status": "running",
      "connection_id": "conn_01jq8k2m3n4p5q6r7s8t9u0v1",
      "pipeline_id": "pl_01hqxyz",
      "created_at": "2025-03-27T14:22:10Z",
      "started_at": "2025-03-27T14:22:12Z"
    }
  ],
  "meta": {
    "page": { "limit": 50, "cursor": null }
  }
}
```

***

## Get job status and details

```http theme={null}
GET /api/v1/jobs/{jobId}
```

**Response `200 OK`**

```json theme={null}
{
  "data": {
    "id": "job_01jq8syncabc",
    "type": "connection_sync",
    "status": "running",
    "connection_id": "conn_01jq8k2m3n4p5q6r7s8t9u0v1",
    "pipeline_id": "pl_01hqxyz",
    "created_at": "2025-03-27T14:22:10Z",
    "queued_at": "2025-03-27T14:22:10Z",
    "started_at": "2025-03-27T14:22:12Z",
    "finished_at": null,
    "duration_seconds": null,
    "metrics": {
      "rows_extracted": 45210,
      "rows_loaded": 44802,
      "bytes_read": 67108864
    },
    "error": null
  }
}
```

***

## Get job results

```http theme={null}
GET /api/v1/jobs/{jobId}/results
```

Returns summary result payloads after the job reaches a terminal state. While running, the API may return **409 Conflict** or a partial stub depending on deployment policy.

**Response `200 OK`**

```json theme={null}
{
  "data": {
    "job_id": "job_01jq8syncabc",
    "status": "succeeded",
    "tables": [
      {
        "name": "public.orders",
        "rows_loaded": 44802,
        "checksum": "sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
      }
    ],
    "warnings": [
      {
        "code": "column_type_widened",
        "message": "Column discount_pct coerced from NUMERIC(5,2) to NUMERIC(8,4)"
      }
    ]
  }
}
```

***

## Cancel a running job

```http theme={null}
POST /api/v1/jobs/{jobId}/cancel
```

Idempotent for already-terminal jobs: returns the current job state.

**Response `200 OK`**

```json theme={null}
{
  "data": {
    "id": "job_01jq8syncabc",
    "status": "cancelling",
    "cancel_requested_at": "2025-03-27T14:30:00Z"
  }
}
```

***

## Webhooks

Manage outbound webhook subscriptions for job lifecycle and orchestration events.

### List webhook subscriptions

```http theme={null}
GET /api/v1/webhooks
```

**Response `200 OK`**

```json theme={null}
{
  "data": [
    {
      "id": "wh_01jq8hook01",
      "url": "https://hooks.slack.com/services/T000/B000/XXXXXXXX",
      "events": ["job.succeeded", "job.failed", "backfill.completed"],
      "enabled": true,
      "created_at": "2025-03-20T09:00:00Z"
    }
  ]
}
```

### Create webhook subscription

```http theme={null}
POST /api/v1/webhooks
```

**Request body**

```json theme={null}
{
  "url": "https://api.internal.example.com/planasonix/webhook",
  "events": ["job.succeeded", "job.failed"],
  "secret": "whsec_custom_optional_if_server_generates"
}
```

**Response `201 Created`**

```json theme={null}
{
  "data": {
    "id": "wh_01jq8hook02",
    "url": "https://api.internal.example.com/planasonix/webhook",
    "events": ["job.succeeded", "job.failed"],
    "signing_secret": "whsec_01jq8secretxxxxxxxxxxxxxxxx",
    "enabled": true,
    "created_at": "2025-03-27T15:00:00Z"
  }
}
```

<Tip>
  Verify deliveries using the `X-Planasonix-Signature` header (HMAC over the raw body with your signing secret). Store `signing_secret` in your vault immediately; it may not be returned on later `GET` calls.
</Tip>

### Get webhook details

```http theme={null}
GET /api/v1/webhooks/{id}
```

**Response `200 OK`**

```json theme={null}
{
  "data": {
    "id": "wh_01jq8hook02",
    "url": "https://api.internal.example.com/planasonix/webhook",
    "events": ["job.succeeded", "job.failed"],
    "enabled": true,
    "last_delivery_at": "2025-03-27T14:55:01Z",
    "failure_count_24h": 0,
    "created_at": "2025-03-27T15:00:00Z"
  }
}
```

### Update webhook

```http theme={null}
PUT /api/v1/webhooks/{id}
```

**Request body**

```json theme={null}
{
  "url": "https://api.internal.example.com/v2/planasonix/webhook",
  "events": ["job.succeeded", "job.failed", "backfill.completed"],
  "enabled": true
}
```

**Response `200 OK`**

```json theme={null}
{
  "data": {
    "id": "wh_01jq8hook02",
    "url": "https://api.internal.example.com/v2/planasonix/webhook",
    "events": ["job.succeeded", "job.failed", "backfill.completed"],
    "enabled": true,
    "updated_at": "2025-03-27T15:05:00Z"
  }
}
```

### Delete webhook

```http theme={null}
DELETE /api/v1/webhooks/{id}
```

**Response `204 No Content`**

### Rotate signing secret

```http theme={null}
POST /api/v1/webhooks/{id}/rotate-secret
```

**Response `200 OK`**

```json theme={null}
{
  "data": {
    "id": "wh_01jq8hook02",
    "signing_secret": "whsec_01jq8newsecretyyyyyyyyyyyyy",
    "rotated_at": "2025-03-27T15:10:00Z"
  }
}
```

### Send test event

```http theme={null}
POST /api/v1/webhooks/{id}/test
```

**Request body (optional)**

```json theme={null}
{
  "event": "job.succeeded"
}
```

**Response `202 Accepted`**

```json theme={null}
{
  "data": {
    "delivery_id": "whd_01jq8test01",
    "status": "pending"
  }
}
```

### List delivery history

```http theme={null}
GET /api/v1/webhooks/{id}/deliveries
```

**Response `200 OK`**

```json theme={null}
{
  "data": [
    {
      "id": "whd_01jq8del001",
      "event": "job.succeeded",
      "status": "delivered",
      "http_status": 200,
      "attempt": 1,
      "delivered_at": "2025-03-27T14:55:01Z"
    },
    {
      "id": "whd_01jq8del002",
      "event": "job.failed",
      "status": "failed",
      "http_status": 503,
      "attempt": 3,
      "last_error": "connection reset by peer",
      "delivered_at": null
    }
  ]
}
```

***

## OpenAPI specification

```http theme={null}
GET /api/v1/openapi.yaml
```

Returns the **OpenAPI 3** document describing this v1 orchestration surface. Use it with codegen tools or import into Postman.

**Response `200 OK`**

`Content-Type: application/yaml` with the full specification body.

***

## Related topics

<CardGroup cols={2}>
  <Card title="Authentication" icon="key" href="/api-reference/authentication">
    Bearer tokens and scopes.
  </Card>

  <Card title="Webhooks (product)" icon="link" href="/orchestration/webhooks">
    Event types and UI configuration.
  </Card>
</CardGroup>
