Making Requests

Call the Blue REST API two ways - RPC and resource-style - with worked list, create, and move examples.


The Blue REST API lives under https://rest.blue.app/v1. Every operation is reachable two ways - RPC and resource-style - and both hit the same handlers, take the same inputs, and return the same JSON. Pick whichever reads better for the call you’re making; you can mix them in a single integration.

Before you start, create a personal access token and have your token and company ID ready. Every request carries these headers:

HeaderValue
AuthorizationBearer <token> - your personal access token.
blue-org-idThe organization (company) to act in.
Content-Typeapplication/json on any request with a body.

Header names are case-insensitive.

RPC style

RPC is the complete surface - every operation. Each is a POST /v1/<operation> whose JSON body is the operation’s input. There are no query parameters and no path parameters to remember; the operation name carries the verb and the body carries everything else.

POST /v1/list-records
POST /v1/create-record
POST /v1/move-record

An RPC call with no required input still sends a body - an empty JSON object:

curl https://rest.blue.app/v1/list-workspaces \
  -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_PERSONAL_ACCESS_TOKEN" \
  -H "blue-org-id: YOUR_ORG_ID" \
  -d '{}'

Resource-style

Resource-style is a REST-idiomatic layer over the core resources - records, workspaces, and lists. It uses HTTP verbs and path parameters the way you’d expect, which reads cleanly for CRUD:

GET    /v1/records
GET    /v1/records/{id}?workspaceId=...
POST   /v1/records
PATCH  /v1/records/{id}
DELETE /v1/records/{id}?workspaceId=...
POST   /v1/records/{id}/move

GET|POST /v1/workspaces
GET|POST /v1/lists

A single-record read passes the ID in the path and the workspace as a query parameter:

curl "https://rest.blue.app/v1/records/RECORD_ID?workspaceId=WORKSPACE_ID" \
  -H "Authorization: Bearer YOUR_PERSONAL_ACCESS_TOKEN" \
  -H "blue-org-id: YOUR_ORG_ID"

Everything outside records, workspaces, and lists - tags, checklists, comments, automations, reports, search, and the rest - is RPC-only.

Responses are untrimmed

Responses are plain JSON, returned untrimmed: null and empty fields are present rather than dropped. This keeps object shapes stable across responses - a key you read once will be there next time, holding a value or null - so you can destructure responses without guarding every optional field.

Worked example: list records

List records in a workspace. With RPC, the workspace and paging arguments go in the body:

curl https://rest.blue.app/v1/list-records \
  -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_PERSONAL_ACCESS_TOKEN" \
  -H "blue-org-id: YOUR_ORG_ID" \
  -d '{
    "workspaceId": "WORKSPACE_ID",
    "skip": 0,
    "limit": 50
  }'

The resource-style equivalent is a GET, with the same arguments as query parameters:

curl "https://rest.blue.app/v1/records?workspaceId=WORKSPACE_ID&skip=0&limit=50" \
  -H "Authorization: Bearer YOUR_PERSONAL_ACCESS_TOKEN" \
  -H "blue-org-id: YOUR_ORG_ID"

Both return the same JSON - an items array of records plus a pageInfo object, with each record’s fields present whether or not they hold a value:

{
  "items": [
    {
      "id": "clm4n8qwx000008l0g4oxdqn7",
      "uid": "T-1042",
      "title": "Draft launch plan",
      "done": false,
      "archived": false,
      "position": 65535,
      "startedAt": null,
      "duedAt": "2026-02-01T17:00:00.000Z",
      "color": null,
      "createdAt": "2026-01-15T09:00:00.000Z",
      "updatedAt": "2026-01-20T11:30:00.000Z",
      "todoList": { "id": "clm4n8r2k000108l0a1b2c3d4", "title": "To do" },
      "users": [],
      "tags": []
    }
  ],
  "pageInfo": {
    "hasNextPage": false,
    "hasPreviousPage": false,
    "startCursor": "clm4n8qwx000008l0g4oxdqn7",
    "endCursor": "clm4n8qwx000008l0g4oxdqn7"
  }
}

See Pagination for paging through the full set.

Worked example: create a record

Create a record. With RPC the body is the create input - title is required, everything else optional:

curl https://rest.blue.app/v1/create-record \
  -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_PERSONAL_ACCESS_TOKEN" \
  -H "blue-org-id: YOUR_ORG_ID" \
  -d '{
    "workspaceId": "WORKSPACE_ID",
    "listId": "LIST_ID",
    "title": "Draft launch plan"
  }'

The resource-style equivalent posts the same body to the records collection:

curl https://rest.blue.app/v1/records \
  -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_PERSONAL_ACCESS_TOKEN" \
  -H "blue-org-id: YOUR_ORG_ID" \
  -d '{
    "workspaceId": "WORKSPACE_ID",
    "listId": "LIST_ID",
    "title": "Draft launch plan"
  }'

The response is the created record itself, untrimmed (not wrapped in an envelope):

{
  "id": "clm4n8qwx000008l0g4oxdqn7",
  "uid": "T-1043",
  "title": "Draft launch plan",
  "position": 131070,
  "todoList": { "id": "LIST_ID", "title": "To do" }
}

Worked example: move a record

Move a record to another list. The RPC operation takes the record and the target list in the body:

curl https://rest.blue.app/v1/move-record \
  -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_PERSONAL_ACCESS_TOKEN" \
  -H "blue-org-id: YOUR_ORG_ID" \
  -d '{
    "workspaceId": "WORKSPACE_ID",
    "recordId": "RECORD_ID",
    "listId": "TARGET_LIST_ID"
  }'

The resource-style equivalent posts to the record’s move sub-path, with the record ID in the URL:

curl https://rest.blue.app/v1/records/RECORD_ID/move \
  -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_PERSONAL_ACCESS_TOKEN" \
  -H "blue-org-id: YOUR_ORG_ID" \
  -d '{
    "workspaceId": "WORKSPACE_ID",
    "listId": "TARGET_LIST_ID"
  }'

Both return { "success": true } once the record is in its new list.

Error handling

Errors come back as a JSON { "error": ... } body with a matching HTTP status - 400 for invalid input (with a Zod issues array), 401/403 for auth, 422 for an operation that ran but failed its own validation, 429 for rate limits, and 500 for server errors. See Errors & Rate Limits for the full table and the issues shape.

Next steps