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

> ## Agent Instructions
> Video walkthroughs: the Dimension product overview video (https://www.youtube.com/watch?v=Mx8rg5wfkU0, also embedded at https://docs.dimension.io/overview) is the first of a series. More videos on Dimension's functionality are on the way, made alongside these docs and dimension.io. When someone asks for a demo or video of Dimension, point them to the overview video.

# JSON Path Reference

> Data Path, ID Path, Capture Variables, and pagination JSON paths with worked examples

The Data Feed editor uses **JSON paths** in three places: **Data Path**, **ID Path**, and **Capture Variables**. Pagination patterns also use them. This page is a reference for the syntax with copy-paste examples.

## Syntax recap

| Notation | Means |
| - | - |
| `$` | The root of the JSON response |
| `.` | Property access (`$.items` → `items` property of root) |
| `[*]` | Wildcard across array elements (`$.items[*].id` → all `id`s) |
| `[0]` | First element of an array |
| `[-1]` | Last element of an array |

Dimension follows the **JSONPath** convention. The root prefix `$` is optional in most fields — `items` and `$.items` are equivalent.

## Data Path — pointing at the records array

The **Data Path** is the JSONPath to the array of records inside the response. Composer infers it on the first successful run but you can override.

### Example responses + their Data Paths

```json theme={null}
// Response 1: items at the root
[
  { "id": "1", "name": "alice" },
  { "id": "2", "name": "bob" }
]
```

**Data Path:** `$` (root)

```json theme={null}
// Response 2: items nested under a key
{
  "items": [
    { "id": "1", "name": "alice" },
    { "id": "2", "name": "bob" }
  ],
  "next_cursor": "xyz"
}
```

**Data Path:** `items` (or `$.items`)

```json theme={null}
// Response 3: deeply nested
{
  "data": {
    "users": {
      "edges": [
        { "node": { "id": "1", "name": "alice" } },
        { "node": { "id": "2", "name": "bob" } }
      ]
    }
  }
}
```

**Data Path:** `data.users.edges[*].node`

This last form is common in GraphQL responses.

## ID Path — the unique key per record

The **ID Path** is the JSONPath to a unique identifier on each record. Used for **deduplication across syncs** and as the join key for **Detail Requests**.

Given records from Response 1 / 2 above:

```json theme={null}
{ "id": "1", "name": "alice" }
```

**ID Path:** `id`

Given records like:

```json theme={null}
{
  "user": {
    "id": "user-123",
    "email": "alice@example.com"
  }
}
```

**ID Path:** `user.id` (or `user.email` if email is more stable)

Given records with **composite keys**:

```json theme={null}
{
  "device": "LAPTOP-01",
  "scan_date": "2026-05-19"
}
```

Composer can synthesise an ID:

```text theme={null}
"The records don't have a single unique field. Use device + scan_date
as a composite key — hash them together."
```

Composer adds a transform step that produces `<device>::<scan_date>` and uses it as the ID Path.

## Capture Variables — pulling values for Detail Requests

The **Capture Variables** section exposes JSONPath-extracted values for use in the **Detail Request** URL / body / params.

### Example — slug-based detail endpoint

Primary endpoint returns:

```json theme={null}
[
  { "id": "1", "slug": "alice-smith", "name": "Alice Smith" },
  { "id": "2", "slug": "bob-jones",   "name": "Bob Jones" }
]
```

You want the detail endpoint to use `slug` not `id`:

| Variable name | JSONPath | Used as |
| - | - | - |
| `id` | `id` | (default) |
| `slug` | `slug` | `{{slug}}` in the Detail Request URL |

Detail Request URL becomes:

```text theme={null}
https://api.example.com/users/{{slug}}/full
```

### Example — captured context

Primary endpoint returns a list with parent context:

```json theme={null}
{
  "tenant_id": "acme",
  "users": [
    { "id": "1", "name": "alice" },
    { "id": "2", "name": "bob" }
  ]
}
```

You want every Detail Request to include the tenant in the URL:

| Variable name | JSONPath | Source |
| - | - | - |
| `id` | `users[*].id` | Per-record |
| `tenant_id` | `tenant_id` | Response root, captured once |

Detail Request URL:

```text theme={null}
https://api.example.com/{{tenant_id}}/users/{{id}}
```

## Pagination JSONPaths

When Pagination is set to **Next Page Link** or **Cursor**, Composer asks for the JSONPath to the cursor/next URL inside the response.

### Next Page Link — Response with `next_url`

```json theme={null}
{
  "data": [ /* ... */ ],
  "meta": {
    "pagination": {
      "next_url": "https://api.example.com/devices?page=2"
    }
  }
}
```

**Next URL JSONPath:** `meta.pagination.next_url`

When the field returns `null` or is missing, Dimension stops pagination automatically.

### Next Page Link — Link header (GitHub style)

GitHub puts the next URL in the **`Link` HTTP response header**, not the body:

```text theme={null}
Link: <https://api.github.com/orgs/threataware/repos?page=2>; rel="next"
```

Tell Composer:

```text theme={null}
"Pagination is the GitHub Link header / next-page-link pattern.
Extract the URL with rel="next" from the Link header."
```

Composer wires the parser without you needing JSONPath syntax.

### Cursor — Slack style

```json theme={null}
{
  "messages": [ /* ... */ ],
  "response_metadata": {
    "next_cursor": "dXNlcjpVMDYxTkZUVDI="
  }
}
```

| Field | Value |
| - | - |
| Cursor JSONPath (response) | `response_metadata.next_cursor` |
| Cursor param name (request) | `cursor` |
| Stop when | Cursor is empty string or missing |

### Page Number & Size — Index based

For an API that takes `?page=N&size=100`:

| Field | Value |
| - | - |
| Page param name | `page` |
| Page size param name | `size` (or `limit`, `per_page`) |
| Starting page | 1 (or 0, configurable) |
| Stop when | Empty results array |

Composer asks for the response's record count field if you have one (e.g. `total: 4521`) to avoid an extra empty-response call.

### Limit/Offset

For `?limit=100&offset=200`:

| Field | Value |
| - | - |
| Limit param name | `limit` |
| Offset param name | `offset` |
| Default limit | 100 |
| Stop when | Empty results or total reached |

## Worked examples — real API shapes

### GitHub — repos endpoint

```text theme={null}
URL: https://api.github.com/orgs/<org>/repos
Auth: Bearer Token
Pagination: Next Page Link (Link header)
Data Path: $
ID Path: id
Capture Variables: full_name
```

Detail Request to pull repo branches:

```text theme={null}
URL: https://api.github.com/repos/{{full_name}}/branches
Auth: Inherit from parent
```

### Slack — conversations.list

```text theme={null}
URL: https://slack.com/api/conversations.list
Auth: Bearer Token (bot token)
Pagination: Cursor
  - Cursor JSONPath: response_metadata.next_cursor
  - Cursor param: cursor
Data Path: channels
ID Path: id
```

### Microsoft Graph — users

```text theme={null}
URL: https://graph.microsoft.com/v1.0/users
Auth: Bearer Token (OAuth2 client credentials)
Pagination: Next Page Link
  - Next URL JSONPath: '@odata.nextLink'
Data Path: value
ID Path: id
```

Notice the **`@odata.nextLink`** key — JSONPath escapes the `@` automatically.

### Jira — search

```text theme={null}
URL: https://yourorg.atlassian.net/rest/api/3/search?jql=project=SEC
Method: GET
Auth: Basic Auth (email + API token)
Pagination: Limit/Offset
  - Limit param: maxResults
  - Offset param: startAt
Data Path: issues
ID Path: id
```

### Elasticsearch — \_search

```text theme={null}
URL: https://es.example.com/index/_search
Method: POST
Body: {
  "query": { "match_all": {} },
  "size": 100,
  "from": 0
}
Pagination: Limit/Offset
  - Limit param: size (in body)
  - Offset param: from (in body)
Data Path: hits.hits
ID Path: _id
```

## Debugging tips

* **The Test panel shows the response** — use it before fiddling with JSONPaths. Inspect the raw payload to confirm structure.
* **Composer is good at JSONPath** — paste a sample response into the chat and say *"the records live under this path; configure it"*.
* **Inherit auth from parent** when the Detail Request is on the same API — avoids re-entering credentials.
* **Empty Data Path on a single-object response** — when the response itself is the record (e.g. `GET /me`), set Data Path to `$` and Composer treats the whole response as one record.

## See also

> [card] [Data Feeds overview](https://docs.dimension.io/data-feeds.md)
>
> The full editor with Trigger / Data Request / Detail Request

> [card] [Data Sources](https://docs.dimension.io/data-feeds/data-sources.md)
>
> HTTP methods and auth patterns

> [card] [Schema & Fields](https://docs.dimension.io/data-feeds/schema.md)
>
> How Composer infers and refines the schema

> [card] [Sync & Freshness](https://docs.dimension.io/data-feeds/sync.md)
>
> Schedules and troubleshooting


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.