[New] Tables and Records API

Summary

  • Tables and Records let you store structured data in Asana. A Table holds Records (rows), and its columns are custom fields

  • New resource types: Table and Record.

  • New OAuth scopes: tables:read, tables:write, tables:delete, records:read, records:write, records:delete.

  • Records support comments through the existing Stories API (GET/POST /records/{record_gid}/stories).

  • Webhooks (added, changed, deleted, and undeleted) are supported on Tables and Records. Subscribe to changes with POST /webhooks.

Who is affected

  • No breaking changes. These are new resources and new endpoints; no existing endpoint’s request or response shape changed.

  • Developers who want to create and manage Tables and Records via API, instead of only through the Asana web app.

Key behaviors

  • A record belongs to exactly one table. custom_fields values can only be written for fields already present in that table’s custom field settings

  • Only global (published) custom fields can be added to a table. Adding a non-global field, whether reusing an existing field or creating one inline, fails with 403 Forbidden.

Timeline

This is a non-breaking, additive change. The tables and Records endpoints will be available to all developers on October 27, 2026.

Creating a table

POST /tables
{
  "data": {
    "name": "Vendor contracts",
    "workspace": "1331",
    "custom_field_settings": ["14916"]
  }
}

A custom_field_settings entry can be the GID of an existing global custom field, or an inline object creating a new one. This is equivalent to, but more efficient than, creating the table and then calling addCustomFieldSetting once per field.

Adding a custom field to an existing table

POST /tables/{table_gid}/addCustomFieldSetting
{
  "data": {
    "custom_field": "14916",
    "is_important": true
  }
}

Creating a record

POST /records
{
  "data": {
    "name": "Q3 renewal",
    "table": "1337",
    "custom_fields": {
      "5678904321": "On Hold"
    },
    "unique_fingerprint": "sfdc-opportunity-0061"
  }
}

Only custom fields already present in the record’s table can be set through custom_fields.

Listing records in a table

GET /tables/{table_gid}/records?limit=50&sort_by=created_at

{
  "data": [
    {
      "gid": "1203",
      "resource_type": "record",
      "name": "Q3 renewal"
    },
    {
      "gid": "1204",
      "resource_type": "record",
      "name": "Q4 renewal"
    }
  ],
  "next_page": {"offset": "...", "path": "...", "uri": "..."}
}

Searching records in a workspace

GET /workspaces/{workspace_gid}/records/search?text=Renewal&tables.any=12345,23456
{
  "data": [
    {
      "gid": "1203",
      "resource_type": "record",
      "name": "Q3 renewal"
    },
    {
      "gid": "1204",
      "resource_type": "record",
      "name": "Q4 renewal"
    }
  ]
}

Supported endpoints

Method Route
GET /workspaces/{workspace_gid}/tables
POST /tables
GET /tables/{table_gid}
PUT /tables/{table_gid}
DELETE /tables/{table_gid}
POST /tables/{table_gid}/addCustomFieldSetting
POST /tables/{table_gid}/removeCustomFieldSetting
GET /tables/{table_gid}/custom_field_settings
GET /tables/{table_gid}/records
POST /records
GET /records/{record_gid}
PUT /records/{record_gid}
DELETE /records/{record_gid}
GET /workspaces/{workspace_gid}/records/search
GET /records/{record_gid}/stories
POST /records/{record_gid}/stories

Error codes

Status Message
400 Bad Request Missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 Unauthorized A valid authentication token was not provided with the request.
402 Payment Required Advanced record search requires a premium Asana plan.
403 Forbidden Custom field already exists on the table.
403 Forbidden Cannot add a non-global (unpublished) custom field to a table.
403 Forbidden You cannot add any more custom fields to this table. The limit for custom fields on a table is 150.
404 Not Found The requested table or record does not exist, or you don’t have access to it.
409 Conflict A record with the given unique_fingerprint already exists in this table. A unique_fingerprint is a unique ID you choose for a record, such as its ID in another system. This conflict happens when you create a second record with the same value in the same table. This prevents retried or repeated imports from creating duplicate records in a table.
429 Too Many Requests You have exceeded one of the enforced rate limits in the API.
500 Internal Server Error There was a problem on Asana’s end.

Resources

Questions and Feedback

If you have questions or feedback about the Tables and Records API, please reply to this post.

4 Likes

Excellent - thanks, @Darya_Selivanava!

Thanks Darya, great to see this live.

One gap we ran into last week: there’s no endpoint for reading or changing record permissions (privacy or record members). The help article confirms access can be set per record, but there’s no way to change it in bulk, either in the UI or through the API.

A customer in pilot had 500+ records where users who could see the table (set to public to org) couldn’t find records they hadn’t created when searching in a reference custom field.
Making each record visible to the organisation fixed it, but it had to be done manually (ended up using Claude to interact with the browser and go through record by record - not ideal).

There wasn’t an option we could find in the API and therefore scripting to be able to achieve it, nor a bulk update option in the UI.

Are record permission endpoints, or a table-level default for new records, on the roadmap?

2 Likes