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

# Query parameters

> Encoding objects and arrays in Inbox API query parameters

## Overview

The Inbox API uses bracket notation for encoding complex data types (objects and arrays) in query parameters. This guide covers the encoding rules and common patterns.

## Objects

Objects are encoded using bracket notation with property names.

**Object value:**

```json theme={null}
{
  "id": "l44e15irdq4db30i77cgphhx",
  "timestamp": "2025-01-15T10:30:00.000Z"
}
```

**Encoded in URL:**

```
?cursorId=l44e15irdq4db30i77cgphhx&cursorTimestamp=2025-01-15T10:30:00.000Z
```

### Cursor example

<CodeGroup>
  ```typescript cursor.ts theme={null}
  // Preferred: flat cursor parameters
  const { data } = await client.get('/threads', {
    params: {
      cursorId: 'l44e15irdq4db30i77cgphhx',
      cursorTimestamp: '2025-01-15T10:30:00.000Z',
      limit: 50
    }
  });
  ```

  ```javascript cursor.js theme={null}
  // Preferred: flat cursor parameters
  const params = new URLSearchParams();
  params.append('cursorId', 'l44e15irdq4db30i77cgphhx');
  params.append('cursorTimestamp', '2025-01-15T10:30:00.000Z');
  params.append('limit', '50');

  const response = await fetch(
    `https://inboxapp.com/api/v1/threads?${params.toString()}`,
    { headers: { 'Authorization': `Bearer ${token}` } }
  );
  ```

  ```bash cURL theme={null}
  curl "https://inboxapp.com/api/v1/threads?cursorId=l44e15irdq4db30i77cgphhx&cursorTimestamp=2025-01-15T10:30:00.000Z&limit=50" \
    -H "Authorization: Bearer $INBOX_API_TOKEN"
  ```
</CodeGroup>

<Tip>
  Use the flat `cursorId` and `cursorTimestamp` parameters for pagination. The bracket notation form (`cursor[id]`, `cursor[timestamp]`) is also supported but deprecated.
</Tip>

## Arrays

Arrays can be encoded in three ways. All are supported by the API.

**Array value:**

```json theme={null}
["df6jbw4h36qm5d9iu2sgn7kx", "g7ht2kw9xr5m8fp3yjnbqvce"]
```

### Option 1: repeated parameters (simplest)

```
?accountLinkIds=df6jbw4h36qm5d9iu2sgn7kx&accountLinkIds=g7ht2kw9xr5m8fp3yjnbqvce
```

### Option 2: empty bracket notation

Explicitly denotes an array. Useful when there's only one value:

```
?accountLinkIds[]=df6jbw4h36qm5d9iu2sgn7kx&accountLinkIds[]=g7ht2kw9xr5m8fp3yjnbqvce
```

### Option 3: indexed bracket notation

Most explicit. Allows reordering and sparse arrays:

```
?accountLinkIds[0]=df6jbw4h36qm5d9iu2sgn7kx&accountLinkIds[1]=g7ht2kw9xr5m8fp3yjnbqvce
```

## Thread filters

The `GET /threads` endpoint supports advanced filtering via the `filters` parameter using nested bracket notation.

### By tags

```typescript theme={null}
// Threads with ANY of these tags (OR logic)
const { data } = await client.get('/threads', {
  params: {
    'filters[tags][selectedIds][0]': 't8rvk2m5j0xn4wq7ybftcael',
    'filters[tags][selectedIds][1]': 'u9swl3n6k1yo5xr8zcgudbfm'
  }
});

// Threads with NO tags
const { data: untagged } = await client.get('/threads', {
  params: {
    'filters[tags][noTags]': true
  }
});
```

### By status

```typescript theme={null}
// Threads with prospects in specific statuses
const { data } = await client.get('/threads', {
  params: {
    'filters[statuses][selectedIds][0]': 's3km7xj9wq5p2bvnhfdteoly',
    'filters[statuses][selectedIds][1]': 's4ln8yk0xr6q3cw1behsifup'
  }
});

// Threads with no status set
const { data: noStatus } = await client.get('/threads', {
  params: {
    'filters[statuses][noStatus]': true
  }
});
```

### By assignee

```typescript theme={null}
// Threads assigned to specific members
const { data } = await client.get('/threads', {
  params: {
    'filters[assignees][selectedIds][0]': 'r3km7xj9wq5p2bvnhfdteoly'
  }
});

// Unassigned threads only
const { data: unassigned } = await client.get('/threads', {
  params: {
    'filters[assignees][noAssignee]': true
  }
});
```

### By campaign

```typescript theme={null}
// Threads from specific campaigns
const { data } = await client.get('/threads', {
  params: {
    'filters[campaigns][selectedIds][0]': 'c5nwp8r2j3ym6at0xbkqhfvg'
  }
});

// Threads not from any campaign
const { data: noCampaign } = await client.get('/threads', {
  params: {
    'filters[campaigns][noCampaign]': true
  }
});
```

### Sort order

```typescript theme={null}
const { data } = await client.get('/threads', {
  params: {
    'filters[sortOrder]': 'desc' // or 'asc'
  }
});
```

## Combined filters

Multiple filters combine with AND logic:

```typescript theme={null}
// Active threads + qualified status + assigned to specific member
const { data } = await client.get('/threads', {
  params: {
    inbox: 'default',
    'accountLinkIds[0]': 'df6jbw4h36qm5d9iu2sgn7kx',
    'filters[statuses][selectedIds][0]': 's3km7xj9wq5p2bvnhfdteoly',
    'filters[assignees][selectedIds][0]': 'r3km7xj9wq5p2bvnhfdteoly',
    limit: 50
  }
});
```

## Filter parameter reference

| Filter              | Parameter                            | Description                                                |
| ------------------- | ------------------------------------ | ---------------------------------------------------------- |
| Tags (include)      | `filters[tags][selectedIds][N]`      | Show threads with any of these tag IDs                     |
| Tags (exclude)      | `filters[tags][noTags]`              | Show only untagged threads                                 |
| Statuses (include)  | `filters[statuses][selectedIds][N]`  | Show threads with any of these status IDs                  |
| Statuses (exclude)  | `filters[statuses][noStatus]`        | Show only threads with no status                           |
| Assignees (include) | `filters[assignees][selectedIds][N]` | Show threads assigned to these member IDs                  |
| Assignees (exclude) | `filters[assignees][noAssignee]`     | Show only unassigned threads                               |
| Campaigns (include) | `filters[campaigns][selectedIds][N]` | Show threads from these campaigns                          |
| Campaigns (exclude) | `filters[campaigns][noCampaign]`     | Show only non-campaign threads                             |
| Sort order          | `filters[sortOrder]`                 | `"desc"` (newest first, default) or `"asc"` (oldest first) |
| Inbox view          | `inbox`                              | `"default"`, `"no-reply"`, `"requests"`, or `"archived"`   |
| Account links       | `accountLinkIds[N]`                  | Filter by specific account link IDs                        |
| Page size           | `limit`                              | `1`–`100` (default `20`)                                   |

## URL encoding special characters

Values containing special characters must be URL-encoded:

| Character | Encoded      |
| --------- | ------------ |
| Space     | `%20` or `+` |
| `+`       | `%2B`        |
| `&`       | `%26`        |
| `=`       | `%3D`        |
| `[`       | `%5B`        |
| `]`       | `%5D`        |
| `:`       | `%3A`        |

Most HTTP clients handle this automatically.

## Helper function

A utility for encoding filter parameters:

```typescript theme={null}
function buildFilterParams(filters: {
  tagIds?: string[];
  statusIds?: string[];
  assigneeIds?: string[];
  campaignIds?: string[];
  noTags?: boolean;
  noStatus?: boolean;
  noAssignee?: boolean;
  noCampaign?: boolean;
  sortOrder?: 'asc' | 'desc';
}): Record<string, string | boolean> {
  const params: Record<string, string | boolean> = {};

  filters.tagIds?.forEach((id, i) => {
    params[`filters[tags][selectedIds][${i}]`] = id;
  });
  filters.statusIds?.forEach((id, i) => {
    params[`filters[statuses][selectedIds][${i}]`] = id;
  });
  filters.assigneeIds?.forEach((id, i) => {
    params[`filters[assignees][selectedIds][${i}]`] = id;
  });
  filters.campaignIds?.forEach((id, i) => {
    params[`filters[campaigns][selectedIds][${i}]`] = id;
  });

  if (filters.noTags) params['filters[tags][noTags]'] = true;
  if (filters.noStatus) params['filters[statuses][noStatus]'] = true;
  if (filters.noAssignee) params['filters[assignees][noAssignee]'] = true;
  if (filters.noCampaign) params['filters[campaigns][noCampaign]'] = true;
  if (filters.sortOrder) params['filters[sortOrder]'] = filters.sortOrder;

  return params;
}

// Usage
const { data } = await client.get('/threads', {
  params: {
    inbox: 'default',
    ...buildFilterParams({
      tagIds: ['t8rvk2m5j0xn4wq7ybftcael'],
      noAssignee: true
    }),
    limit: 50
  }
});
```

## Common mistakes

<Warning>
  **Wrong:** Using incorrect filter key structure

  ```typescript theme={null}
  // These parameter names are WRONG
  const { data } = await client.get('/threads', {
    params: {
      'filter[tagIds][0]': 'tag_abc',           // Wrong key
      'filter[assignedToMemberIds][0]': 'member_abc', // Wrong key
      'filter[noAssignee]': true,               // Wrong key
      inboxView: 'default',                     // Wrong param name
      accountLinkId: 'acc_abc',                 // Wrong param name
    }
  });
  ```

  **Correct:**

  ```typescript theme={null}
  const { data } = await client.get('/threads', {
    params: {
      'filters[tags][selectedIds][0]': 't8rvk2m5j0xn4wq7ybftcael',
      'filters[assignees][selectedIds][0]': 'r3km7xj9wq5p2bvnhfdteoly',
      'filters[assignees][noAssignee]': true,
      inbox: 'default',
      'accountLinkIds[0]': 'df6jbw4h36qm5d9iu2sgn7kx',
    }
  });
  ```
</Warning>

## Related

<CardGroup cols={2}>
  <Card title="Pagination" icon="book-open" href="/reference/pagination">
    Cursor-based pagination patterns
  </Card>

  <Card title="Managing threads" icon="messages-square" href="/guides/managing-threads">
    Thread filtering examples
  </Card>

  <Card title="List threads" icon="list" href="/api-reference/threads/list-threads">
    Threads endpoint reference
  </Card>

  <Card title="API introduction" icon="code" href="/api-reference/introduction">
    API overview and encoding rules
  </Card>
</CardGroup>
