# Pages

Push, list, and delete pages on a **source**. After you push content, Datalumo indexes it in the background. Successful writes return **202** and the page stays in `pending` until indexing finishes.

You can identify a source by **slug** or **public id**.

**Base path:** `/api/v1/{organisation}/sources/{source}/pages`

---

## List pages

```
GET /sources/{source}/pages
```

**Permission:** `pages.read`

### Query parameters

| Param | Type | Default | Description |
|---|---|---|---|
| `external_id` | string | | Only this external id |
| `status` | string | | Filter by status, e.g. `pending`, `indexed`, `failed` |
| `cursor` | string | | Cursor from a previous response |
| `per_page` | int | `25` | Page size, 1-100 |

### Response

Results are cursor-paginated:

```json
{
  "data": [
    {
      "id": "0194b…",
      "external_id": "wp-42",
      "name": "Hello world",
      "source_type": "api_push",
      "source_url": "https://example.com/hello-world",
      "status": "indexed",
      "excluded": false,
      "error": null,
      "size_bytes": 1234,
      "meta": { "author": "Jane" },
      "created_at": "2026-07-01T12:00:00+00:00",
      "updated_at": "2026-07-01T12:01:00+00:00"
    }
  ],
  "links": { "first": "…", "last": null, "prev": null, "next": "…" },
  "meta": { "path": "…", "per_page": 25, "next_cursor": "…", "prev_cursor": null }
}
```

---

## Create or update a page

```
POST /sources/{source}/pages
```

**Permission:** `pages.write`  
**Status:** `202 Accepted`

If you send the same `external_id` again, Datalumo updates that page and re-indexes it. Sending the exact same content for a page that is already indexed does nothing.

### Body

| Field | Type | Required | Description |
|---|---|---|---|
| `content` | string | yes | Full page body |
| `external_id` | string | no | Stable id from your system (post id, path, and so on). Recommended so updates replace the right page. |
| `name` | string | no | Display title |
| `content_mime` | string | no | Defaults to `text/plain`. Supported: `text/plain`, `text/markdown`, `text/html`. Prefer HTML or Markdown for pushed content. |
| `source_url` | string | no | Public URL used in citations |
| `meta` | object | no | Extra JSON metadata stored with the page |

### Example

```bash
curl -sS -X POST \
  -H "Authorization: Bearer $DATALUMO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "external_id": "wp-42",
    "name": "Hello world",
    "content": "# Hello world\n\nBody…",
    "content_mime": "text/markdown",
    "source_url": "https://example.com/hello-world",
    "meta": { "author": "Jane" }
  }' \
  "https://datalumo.app/api/v1/{organisation}/sources/docs/pages"
```

### Response

```json
{
  "data": {
    "id": "0194b…",
    "external_id": "wp-42",
    "name": "Hello world",
    "source_type": "api_push",
    "status": "pending",
    "…"
  }
}
```

If your plan's page limit is full, the request is rejected before the page is created.

---

## Batch create or update

```
POST /sources/{source}/pages/batch
```

**Permission:** `pages.write`  
**Status:** `202 Accepted`  
**Limit:** 1-50 pages per request

### Body

```json
{
  "pages": [
    {
      "external_id": "wp-42",
      "content": "…",
      "content_mime": "text/markdown"
    },
    {
      "external_id": "wp-43",
      "content": "…"
    }
  ]
}
```

Each item uses the same fields as the single-page endpoint. The response is a list of page objects.

---

## Delete a page

```
DELETE /sources/{source}/pages/{page}
```

**Permission:** `pages.write`  
**Status:** `204 No Content`

`{page}` can be:

- the page's public id (UUID), or
- the `external_id` you sent when you pushed it

Unknown ids return **404**.
