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,
      "rank": "a0V",
      "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",
      "list": { "id": "clm4n8r2k000108l0a1b2c3d4", "title": "To do" },
      "users": [],
      "tags": []
    }
  ],
  "pageInfo": {
    "hasNextPage": false,
    "hasPreviousPage": false,
    "startCursor": "clm4n8qwx000008l0g4oxdqn7",
    "endCursor": "clm4n8qwx000008l0g4oxdqn7"
  }
}

See Pagination for paging through the full set.

Create a record in a new database

Use the database operations for a new database. The older create-record and create-list operations require the workspace’s original list setup.

  1. Call list-databases with workspaceId to find the database ID.
  2. Call list-database-fields with workspaceId, databaseId and forView: true to read visible fields and their option IDs.
  3. Call create-database-record with the database ID and initial values.

For example, send this body to POST /v1/create-database-record:

{
  "workspaceId": "WORKSPACE_ID",
  "databaseId": "DATABASE_ID",
  "title": "Draft launch plan",
  "groupOptionId": "STAGE_OPTION_ID",
  "fieldValues": [
    { "fieldId": "NOTES_FIELD_ID", "kind": "text", "value": { "text": "First draft" } }
  ]
}

groupOptionId selects a column option from a single-select field in this database. For a database with an original list field, use an option from that field. You can also omit groupOptionId and set the selection through fieldValues: { "fieldId": "STAGE_FIELD_ID", "kind": "select", "value": ["STAGE_OPTION_ID"] }.

To update a field, send this body to POST /v1/set-database-field-value:

{
  "workspaceId": "WORKSPACE_ID",
  "databaseId": "DATABASE_ID",
  "recordId": "RECORD_ID",
  "fieldId": "NOTES_FIELD_ID",
  "kind": "text",
  "value": { "text": "Revised draft" },
  "clientMutationId": "UNIQUE_WRITE_ID",
  "entitySequence": 1
}

Use a new mutation ID for each edit and increase the sequence for each record/field. Retry an identical request with the same mutation ID and sequence. ACCEPTED confirms the write; REPLAYED confirms an earlier accepted request. Inspect other outcomes before treating the edit as successful.

These operation names and inputs also work through MCP. The GraphQL equivalents are createRecordInDatabase and syncSetRecordFieldValue.

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",
  "rank": "a0V",
  "list": { "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