Skip to content

Administrative API

The Sheetlabs Administrative API lets administrators automate the work normally done in the Sheetlabs web application: managing data tables and their records, configuring published APIs, and managing users and groups.

Authentication and permissions

Use HTTP Basic authentication with an email address and an authentication token in place of the password. See authentication and authorisation for how to obtain and use a token.

All examples use https://sheetlabs.com as the base URL and an authentication token:

curl -u "admin@example.com:t_your_token" https://sheetlabs.com/datatables/ACME

Unless stated otherwise, successful create and update calls return JSON, and successful delete calls return an empty 200 OK response. Errors use an appropriate HTTP status and include a message explaining the problem.

Data models

Data table

Field Type Notes
name string Required. Must be unique within the organisation and contain only alphanumeric characters.
fields array of field objects Required when creating or changing a table. Field names must be unique.
fields[].name string Column name.
fields[].type string One of string, integer, double, boolean, datetime, datetime-dmy, or datetime-mdy.
group.id integer Required. The table's group controls Editor access and the behaviour of APIs with group access. Use List groups to find it.
filename string Specifies the name of the file to import, which must have already been uploaded.
delimiter string The delimeter used between fields for CSVs (typically , or ; or \t).
headersfirstrow boolean Required when importing a file. Specifies whether the first row of the file contains column names.
trimvalues boolean Required when importing a file. Specifies whether to trim leading and trailing whitespace from string fields.
selected_sheet integer Required when importing a file. Specifies the sheet number to import from (zero indexed).
allowempty boolean Specifies whether empty spreadsheets should be permitted (true) or treated as an error (false)
import_mode string replace or append, used when a table update imports data.
googlerefresh boolean Enables automatic one-way refreshes from a linked Google Sheet.
auto_manage_service boolean Only for spreadsheet upload calls. Creates or adjusts a matching API as described below.

An example data-table response, abridged to its important fields:

{
  "id": 15335,
  "name": "products",
  "fields": [
    {"name": "sku", "type": "string", "sqltype": "TEXT"},
    {"name": "price", "type": "double", "sqltype": "FLOAT8"},
    {"name": "active", "type": "boolean", "sqltype": "BOOLEAN"}
  ],
  "group": {"id": 41, "name": "Default"},
  "source": "csv",
  "status": "Ready",
  "rows": 0
}

Records

Records are JSON objects whose keys are the table's field names. Sheetlabs adds the numeric __id field to every stored record. Values submitted to the administrative record endpoints should be strings; they are converted according to the table field type. An empty string is stored as null for non-string fields.

{
  "__id": 9001,
  "sku": "SKU-100",
  "price": 19.99,
  "active": true
}

API configuration

Field Type Notes
name string Required and unique within the organisation. It becomes part of the consumer API URL.
datatable integer Required data-table ID. Changing it also changes the API's group.
description string Description shown in generated consumer documentation.
access string One of public (accessible publicly without auth), authenticated (any authenticated user), group (accessible to users in the same group as the underlying datatable), or private (accessible only to the specified users in the users field).
limit integer Maximum records returned by a consumer API call; use 0 for no configured maximum.
inputs array One item for each filterable data-table field. presence is disallowed, optional, or required; partial enables * matching for strings and range enables .. ranges for numeric/date fields.
outputs array One item for each table field. Set include to control whether the field is returned to consumer API callers.
users array of integers User IDs allowed to call an API with private access.
outputrecordid boolean Indicates whether the records's __id will be included in the API response.
post boolean Indicates whether the API accepts POST requests (for creating records).
put boolean Indicates whether the API accepts PUT requests (for replacing existing records).
patch boolean Indicates whether the API accepts PATCH requests (for partially amending records).
delete boolean Indicates whether the API accepts DELETE requests (for deleting records).

An example complete API response looks like this:

{
  "id": 24680,
  "name": "products",
  "datatable": 15335,
  "access": "authenticated",
  "limit": 100,
  "inputs": [
    {"name": "sku", "type": "string", "presence": "optional", "partial": true, "range": false}
  ],
  "outputs": [
    {"name": "sku", "type": "string", "include": true},
    {"name": "price", "type": "double", "include": true},
    {"name": "active", "type": "boolean", "include": false}
  ],
  "outputrecordid": true,
  "post": false,
  "put": false,
  "patch": false,
  "delete": false,
  "users": []
}

User

Field Type Notes
id integer User ID. Returned by the API; use it as user_id in user endpoints.
forename string Required when creating or replacing a user.
surname string Required when creating or replacing a user.
email string Required and unique.
password string Required on creation unless the organisation has SSO enabled. Optional on update; never returned.
role string Required. One of Admin, Editor, or User.
status string Required. One of Active, Disabled, or Pending Approval.
groups array of group objects The user's groups. Send group objects containing an id; an empty array assigns the default group. List-user responses instead contain group names.
groups[].id integer Group ID to assign. Use List groups to find it.
notify_org_errors boolean Controls whether the user receives organisation error notifications.
token string Returned for an individual user and after user creation. Treat it as a password.
google_authenticated boolean Returned for an individual user; indicates whether a Google account is linked.

Group

Field Type Notes
id integer Group ID. Returned by the API; use it as group_id in group endpoints.
name string Required when creating or updating a group. Must be unique within the organisation.
members array of user objects Required when creating or replacing a group. Send member objects containing an id.
members[].id integer User ID to add to the group. Use List users to find it.

An example group response looks like this:

{
  "id": 41,
  "name": "Default",
  "members": [{"id": 12345}]
}

Data tables

List data tables

GET /datatables/{organisation}

Lists the data tables visible to the authenticated user, ordered by name.

curl -u "admin@example.com:t_your_token" \
  https://sheetlabs.com/datatables/ACME

Response:

[
  {
    "id": 15335,
    "name": "products",
    "source": "csv",
    "status": "Ready",
    "rows": 42,
    "size": 2048,
    "group": "Default",
    "googleRefresh": false
  }
]

Get a data table

GET /datatables/{organisation}/{datatable_id}

Returns the full table definition, including its fields and import settings. Retrieve this before updating a table: a normal metadata update expects the full schema, not a partial patch.

curl -u "admin@example.com:t_your_token" \
  https://sheetlabs.com/datatables/ACME/15335

Response:

{
  "id": 15335,
  "name": "products",
  "fields": [
    {"name": "sku", "type": "string", "sqltype": "TEXT"},
    {"name": "price", "type": "double", "sqltype": "FLOAT8"},
    {"name": "active", "type": "boolean", "sqltype": "BOOLEAN"}
  ],
  "group": {"id": 41, "name": "Default"},
  "source": "csv",
  "status": "Ready",
  "rows": 42
}

Create an empty data table

POST /datatables/{organisation}

Creates an empty table with the supplied schema. This direct endpoint does not import a spreadsheet; use Upload a spreadsheet into a new data table when the data already exists in CSV or XLSX form.

curl -X POST -u "admin@example.com:t_your_token" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "products",
    "fields": [
      {"name": "sku", "type": "string"},
      {"name": "price", "type": "double"},
      {"name": "active", "type": "boolean"}
    ],
    "group": {"id": 41}
  }' \
  https://sheetlabs.com/datatables/ACME

Response:

{
  "id": 15335,
  "name": "products",
  "fields": [
    {"name": "sku", "type": "string", "sqltype": "TEXT"},
    {"name": "price", "type": "double", "sqltype": "FLOAT8"},
    {"name": "active", "type": "boolean", "sqltype": "BOOLEAN"}
  ],
  "group": {"id": 41, "name": "Default"},
  "status": "Ready",
  "rows": 0
}

Update an existing data table

PUT /datatables/{organisation}/{datatable_id}

Updates a table's definition, including its name, fields, group, and Google refresh settings. It is a replacement-style operation: send the complete definition returned by Get a data table, with the changes applied. Omitting fields, for example, is not a safe way to leave fields unchanged.

Adding, removing, or changing fields alters the underlying table. To load data at the same time, set import_mode to replace or append and include compatible import metadata; use the dedicated spreadsheet upload endpoint for CSV/XLSX files. When an import is requested, the new schema must be compatible with the existing table.

curl -X PUT -u "admin@example.com:t_your_token" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "products",
    "fields": [
      {"name": "sku", "type": "string"},
      {"name": "price", "type": "double"},
      {"name": "active", "type": "boolean"},
      {"name": "category", "type": "string"}
    ],
    "group": {"id": 41},
    "googlerefresh": false
  }' \
  https://sheetlabs.com/datatables/ACME/15335

Response:

{
  "id": 15335,
  "name": "products",
  "fields": [
    {"name": "sku", "type": "string"},
    {"name": "price", "type": "double"},
    {"name": "active", "type": "boolean"},
    {"name": "category", "type": "string"}
  ],
  "group": {"id": 41, "name": "Default"},
  "status": "Ready"
}

Delete a data table

DELETE /datatables/{organisation}/{datatable_id}

Deletes the table and all of its records. A table cannot be deleted while any APIs use it; delete those APIs or update them to use another table first. For a Google-backed table, Sheetlabs also stops its automatic refresh notification.

curl -i -X DELETE -u "admin@example.com:t_your_token" \
  https://sheetlabs.com/datatables/ACME/15335

Response:

HTTP/1.1 200 OK

Upload a spreadsheet into a new data table

POST /datatables/upload/{organisation}

Uploads a .csv or .xlsx file and creates a table from it. The request must be multipart/form-data and the file part must be named files. The optional metadata part is a JSON string. If omitted, Sheetlabs detects the table name, headers, field types, delimiter, and sheet; the table is placed in the organisation's default group. If you include metadata.fields, then you can override the field names and types.

You can optionally include auto_manage_service: true in metadata to create a matching API in the same request. The request fails if that API name already exists.

curl -X POST -u "admin@example.com:t_your_token" \
  -F "files=@products.csv" \
  -F 'metadata={"name":"products","auto_manage_service":true}' \
  https://sheetlabs.com/datatables/upload/ACME

Response:

{
  "id": 15335,
  "name": "products",
  "filename": "products.csv",
  "fields": [
    {"name": "sku", "type": "string"},
    {"name": "price", "type": "double"},
    {"name": "active", "type": "boolean"}
  ],
  "group": {"id": 41, "name": "Default"},
  "status": "Ready",
  "rows": 42
}

Replace or append a spreadsheet in an existing data table

PUT /datatables/upload/{organisation}/{datatable_id}

Upload a .csv or .xlsx file and import it into an existing table. Set metadata.import_mode = "replace" (the default) or append.

As with the POST request, you can optionally include auto_manage_service: true in metadata to update an existing matching API if the field names and types change. This is allowed only when the table has at most one API. Sheetlabs rebuilds the table, replaces its data, and, if the API exists, replaces that API's inputs and outputs to match the new fields.

curl -X PUT -u "admin@example.com:t_your_token" \
  -F "files=@products.csv" \
  -F 'metadata={"import_mode":"replace"}' \
  https://sheetlabs.com/datatables/upload/ACME/15335

Response:

{
  "id": 15335,
  "name": "products",
  "filename": "products.csv",
  "status": "Ready",
  "rows": 42
}

Force a Google Sheet refresh

POST /datatables/google_refresh/{datatable_id}?force=true

Queues an asynchronous refresh of a data table linked to Google Sheets. A 200 OK means the refresh was queued, not that the data has already been imported. force=true permits a manual refresh even when automatic refresh is disabled.

curl -X POST -u "admin@example.com:t_your_token" \
  "https://sheetlabs.com/datatables/google_refresh/15335?force=true"

Response:

{
  "code": 200,
  "message": "OK"
}

Records

List records in a data table

GET /records/{organisation}/{datatable_id}

Returns all records as an array of objects, including __id. Add one or more table-field query parameters to apply exact-match filters. Unknown fields return a validation error.

curl -u "admin@example.com:t_your_token" \
  "https://sheetlabs.com/records/ACME/15335?active=true"

Response:

[
  {"__id": 9001, "sku": "SKU-100", "price": 19.99, "active": true},
  {"__id": 9002, "sku": "SKU-101", "price": 24.99, "active": true}
]

Get one record

GET /records/{organisation}/{datatable_id}/{record_id}

Returns one record, including its __id. Currently, requesting an ID that does not exist returns an empty object rather than 404.

curl -u "admin@example.com:t_your_token" \
  https://sheetlabs.com/records/ACME/15335/9001

Response:

{"__id": 9001, "sku": "SKU-100", "price": 19.99, "active": true}

Create a record

POST /records/{organisation}/{datatable_id}

Inserts a record into a data table. Do not send __id: Sheetlabs generates it.

curl -X POST -u "admin@example.com:t_your_token" \
  -H "Content-Type: application/json" \
  -d '{"sku":"SKU-100","price":"19.99","active":"true"}' \
  https://sheetlabs.com/records/ACME/15335

Response:

{"sku": "SKU-100", "price": "19.99", "active": "true"}

Partially update a record

PATCH /records/{organisation}/{datatable_id}/{record_id}

Modifies the specified record_id. It updates only the fields included in the request. At least one field must be supplied. The response echoes the submitted fields; it is not a complete record representation.

curl -X PATCH -u "admin@example.com:t_your_token" \
  -H "Content-Type: application/json" \
  -d '{"price":"24.99"}' \
  https://sheetlabs.com/records/ACME/15335/9001

Response:

{"price": "24.99"}

Replace a record

PUT /records/{organisation}/{datatable_id}/{record_id}

Replaces the record specified by record_id. Supply every field that should retain a value: omitted fields are not preserved. The response includes the __id added by Sheetlabs.

curl -X PUT -u "admin@example.com:t_your_token" \
  -H "Content-Type: application/json" \
  -d '{"sku":"SKU-100","price":"24.99","active":"true"}' \
  https://sheetlabs.com/records/ACME/15335/9001

Response:

{"sku": "SKU-100", "price": "24.99", "active": "true", "__id": "9001"}

Delete a record

DELETE /records/{organisation}/{datatable_id}/{record_id}

Permanently deletes one record.

curl -i -X DELETE -u "admin@example.com:t_your_token" \
  https://sheetlabs.com/records/ACME/15335/9001

Response:

HTTP/1.1 200 OK

Export records as CSV

GET /records/csv/{organisation}/{datatable_id}

Downloads every record in a table as CSV.

curl -u "admin@example.com:t_your_token" \
  -o products.csv \
  https://sheetlabs.com/records/csv/ACME/15335

Response:

sku,price,active
SKU-100,19.99,true
SKU-101,24.99,true

APIs

List APIs

GET /services/{organisation}

Lists APIs visible to the current caller, ordered by name. Use the returned id as api_id in the remaining API-management calls.

curl -u "admin@example.com:t_your_token" \
  https://sheetlabs.com/services/ACME

Response:

[
  {"id": 24680, "name": "products", "group": "Default", "updated": "2026-07-14T12:34:56"}
]

Get a single API

GET /services/{organisation}/{api_id}

Returns the full API configuration for the specified api_id.

curl -u "admin@example.com:t_your_token" \
  https://sheetlabs.com/services/ACME/24680

Response:

{
  "id": 24680,
  "name": "products",
  "datatable": 15335,
  "access": "authenticated",
  "limit": 100,
  "inputs": [{"name": "sku", "type": "string", "presence": "optional", "partial": true, "range": false}],
  "outputs": [{"name": "sku", "type": "string", "include": true}],
  "outputrecordid": true,
  "post": false,
  "put": false,
  "patch": false,
  "delete": false,
  "users": []
}

Create an API

POST /services/{organisation}

Creates a consumer-facing API bound to an existing data table. The configured input and output array positions correspond to the data table's fields.

curl -X POST -u "admin@example.com:t_your_token" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "products",
    "description": "Current products and prices.",
    "datatable": 15335,
    "access": "authenticated",
    "limit": 100,
    "inputs": [
      {"name": "sku", "presence": "optional", "partial": true, "range": false},
      {"name": "price", "presence": "optional", "partial": false, "range": true},
      {"name": "active", "presence": "optional", "partial": false, "range": false}
    ],
    "outputs": [
      {"name": "sku", "include": true},
      {"name": "price", "include": true},
      {"name": "active", "include": false}
    ],
    "outputrecordid": true,
    "post": false,
    "put": false,
    "patch": false,
    "delete": false,
    "users": []
  }' \
  https://sheetlabs.com/services/ACME

Response:

{
  "id": 24680,
  "name": "products",
  "datatable": 15335,
  "access": "authenticated",
  "limit": 100,
  "inputs": [
    {"name": "sku", "type": "string", "presence": "optional", "partial": true, "range": false}
  ],
  "outputs": [
    {"name": "sku", "type": "string", "include": true},
    {"name": "price", "type": "double", "include": true},
    {"name": "active", "type": "boolean", "include": false}
  ],
  "outputrecordid": true,
  "post": false,
  "put": false,
  "patch": false,
  "delete": false,
  "users": []
}

Update an API

PUT /services/{organisation}/{api_id}

Replaces an API's configuration and optionally binds it to a different data table. Send the complete configuration, including inputs, outputs, booleans, and users; this is not a partial update. The API name must remain unique in the organisation.

curl -X PUT -u "admin@example.com:t_your_token" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "products",
    "description": "Current products and prices.",
    "datatable": 15335,
    "access": "public",
    "limit": 100,
    "inputs": [
      {"name": "sku", "presence": "optional", "partial": true, "range": false},
      {"name": "price", "presence": "optional", "partial": false, "range": true},
      {"name": "active", "presence": "optional", "partial": false, "range": false}
    ],
    "outputs": [
      {"name": "sku", "include": true},
      {"name": "price", "include": true},
      {"name": "active", "include": false}
    ],
    "outputrecordid": true,
    "post": true,
    "put": false,
    "patch": false,
    "delete": false,
    "users": []
  }' \
  https://sheetlabs.com/services/ACME/24680

Response:

{
  "id": 24680,
  "name": "products",
  "datatable": 15335,
  "access": "public",
  "limit": 100,
  "outputrecordid": true,
  "post": true,
  "put": false,
  "patch": false,
  "delete": false
}

Delete an API

DELETE /services/{organisation}/{api_id}

Deletes an API. It does not delete the data table or its records.

curl -i -X DELETE -u "admin@example.com:t_your_token" \
  https://sheetlabs.com/services/ACME/24680

Response:

HTTP/1.1 200 OK

Users

List users

GET /users/{organisation}

Lists all users in the organisation, ordered by email. The response supplies the user_id required by the other user endpoints.

curl -u "admin@example.com:t_your_token" \
  https://sheetlabs.com/users/ACME

Response:

[
  {
    "id": 12345,
    "forename": "Avery",
    "surname": "Admin",
    "email": "avery@example.com",
    "role": "Admin",
    "status": "Active",
    "groups": ["Default"]
  }
]

Get a user

GET /users/{organisation}/{user_id}

Returns one user, including group IDs and the user's current token.

curl -u "admin@example.com:t_your_token" \
  https://sheetlabs.com/users/ACME/12345

Response:

{
  "id": 12345,
  "forename": "Avery",
  "surname": "Admin",
  "email": "avery@example.com",
  "role": "Admin",
  "status": "Active",
  "token": "t_example_token",
  "google_authenticated": true,
  "groups": [{"id": 41, "name": "Default"}]
}

Create a user

POST /users/{organisation}

Creates a user. Set emaildetails to true to email account details to the newly created user. A password is required unless SSO is enabled for the organisation. With SSO, Sheetlabs emails the SSO sign-in details instead. Provide group IDs, or send an empty array to assign the default group.

curl -X POST -u "admin@example.com:t_your_token" \
  -H "Content-Type: application/json" \
  -d '{
    "forename": "Jamie",
    "surname": "Editor",
    "email": "jamie@example.com",
    "password": "choose-a-strong-password",
    "role": "Editor",
    "status": "Active",
    "groups": [{"id": 41}],
    "emaildetails": true
  }' \
  https://sheetlabs.com/users/ACME

Response:

{
  "id": 12346,
  "forename": "Jamie",
  "surname": "Editor",
  "email": "jamie@example.com",
  "role": "Editor",
  "status": "Active",
  "token": "t_new_user_token",
  "message": "User created successfully"
}

Update a user

PUT /users/{organisation}/{user_id}

Updates a user's name, email, role, status, optional password, notification setting, and groups.

curl -X PUT -u "admin@example.com:t_your_token" \
  -H "Content-Type: application/json" \
  -d '{
    "forename": "Jamie",
    "surname": "Editor",
    "email": "jamie@example.com",
    "role": "Editor",
    "status": "Active",
    "notify_org_errors": false,
    "groups": [{"id": 41}]
  }' \
  https://sheetlabs.com/users/ACME/12346

Response:

{
  "id": 12346,
  "forename": "Jamie",
  "surname": "Editor",
  "email": "jamie@example.com",
  "role": "Editor",
  "status": "Active",
  "google_authenticated": true,
  "groups": [{"id": 41, "name": "Default"}],
  "message": "User Details Updated successfully."
}

Delete a user

DELETE /users/{organisation}/{user_id}

Deletes a user.

curl -i -X DELETE -u "admin@example.com:t_your_token" \
  https://sheetlabs.com/users/ACME/12346

Response:

HTTP/1.1 200 OK

Groups

List groups

GET /groups/{organisation}

Lists groups visible to the current user. Use the returned ID when assigning a table or user to a group.

curl -u "admin@example.com:t_your_token" \
  https://sheetlabs.com/groups/ACME

Response:

[
  {"id": 41, "name": "Default", "memberCount": 2, "isDefault": true}
]

Get a group

GET /groups/{organisation}/{group_id}

Returns a group and its members.

curl -u "admin@example.com:t_your_token" \
  https://sheetlabs.com/groups/ACME/41

Response:

{
  "id": 41,
  "name": "Default",
  "members": [
    {"id": 12345, "email": "avery@example.com", "role": "Admin", "status": "Active"}
  ]
}

Create a group

POST /groups/{organisation}

Creates a non-default group and assigns its members. The name must be unique in the organisation.

curl -X POST -u "admin@example.com:t_your_token" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Merchandising",
    "members": [{"id": 12345}, {"id": 12346}]
  }' \
  https://sheetlabs.com/groups/ACME

Response:

{
  "id": 42,
  "name": "Merchandising",
  "members": [
    {"id": 12345, "email": "avery@example.com"},
    {"id": 12346, "email": "jamie@example.com"}
  ]
}

Update a group

PUT /groups/{organisation}/{group_id}

Renames a group and replaces its membership with the supplied members list. Members removed from their last group are automatically placed in the default group.

curl -X PUT -u "admin@example.com:t_your_token" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Merchandising",
    "members": [{"id": 12345}]
  }' \
  https://sheetlabs.com/groups/ACME/42

Response:

{
  "id": 42,
  "name": "Merchandising",
  "members": [{"id": 12345, "email": "avery@example.com"}]
}

Delete a group

DELETE /groups/{organisation}/{group_id}

Deletes a non-default group. First move every data table out of the group. Sheetlabs removes the group from members and assigns the default group to anyone who would otherwise have no groups. Default groups cannot be deleted.

curl -i -X DELETE -u "admin@example.com:t_your_token" \
  https://sheetlabs.com/groups/ACME/42

Response:

HTTP/1.1 200 OK