API Reference

Every call of the External Data API with its permission, parameters, a sample request and a sample response. Search to jump to a call. The External Data API guide explains sign-in, permissions and the rules behind them.

⬇ Postman collection ⬇ PDF Every call. For just the calls your secret may use, see your own collection and PDF.
73 calls
Base URL and sign-in Paths start with <base URL>/external/v1/, for example https://your-host/persivapps/service/external/v1/me. Sign in with POST /external/v1/auth/login, then send Authorization: Bearer <access_token>. In the samples, {{base_url}} and {{access_token}} are the Postman collection's variables.

Getting started

POST /external/v1/auth/login #

Sign in with the client ID and secret; returns an access token (1 hour) and a refresh token.

No token: sign-in

Errors follow OAuth 2.0: {"error": "invalid_client" | "invalid_scope", "error_description": "…"}.

ParameterInTypeRequiredDescription
client_idbodystringYesThe client ID
client_secretbodystringYesThe secret (or send both as HTTP Basic)
scopebodystringNoSpace-separated permissions, to get a token with fewer
Sample request
curl -X POST "{{base_url}}/external/v1/auth/login" \
  -H "Content-Type: application/json" \
  -d '{"client_id": "{{client_id}}", "client_secret": "{{client_secret}}"}'
Sample response · 200
{
    "access_token": "pvx_at_9c1f…",
    "token_type": "Bearer",
    "expires_in": 3600,
    "scope": "entries:read entries:write",
    "refresh_token": "pvx_rt_47d2…"
}

POST /external/v1/auth/token #

OAuth 2.0 token endpoint: client_credentials, or refresh_token to renew.

No token: sign-in

ParameterInTypeRequiredDescription
grant_typebodystringYesclient_credentials or refresh_token
refresh_tokenbodystringNoWith refresh_token; each works once
client_idbodystringYesThe client ID
client_secretbodystringYesThe secret
Sample request
curl -X POST "{{base_url}}/external/v1/auth/token" \
  -H "Content-Type: application/json" \
  -d '{"grant_type": "refresh_token", "refresh_token": "{{refresh_token}}", "client_id": "{{client_id}}", "client_secret": "{{client_secret}}"}'
Sample response · 200
{
    "access_token": "pvx_at_5be0…",
    "token_type": "Bearer",
    "expires_in": 3600,
    "scope": "entries:read entries:write",
    "refresh_token": "pvx_rt_a812…"
}

GET /external/v1/me #

This client, the permissions of the current token, and the SmartTables each covers.

Any valid token

Use it to check what a token can do before calling anything else.

Sample request
curl -X GET "{{base_url}}/external/v1/me" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "client": {
        "name": "Acme Logistics",
        "client_id": "ext.acme-logistics.1a2b3c4d"
    },
    "credential": {
        "name": "Production server",
        "expires_at": null
    },
    "permissions": [
        {
            "permission": "entries:read",
            "smarttables": [
                {
                    "id": 61,
                    "name": "Dispatch Log"
                }
            ]
        },
        {
            "permission": "entries:write",
            "smarttables": [
                {
                    "id": 61,
                    "name": "Dispatch Log"
                }
            ]
        }
    ]
}

GET /external/v1/reference/postman #

A Postman collection of just the calls this token may use (?base_url= sets the default host).

Any valid token

Import the file in Postman, fill in client_secret, then run "Sign in".

ParameterInTypeRequiredDescription
base_urlquerystringNoYour API base URL, set as the collection's base_url variable
Sample request
curl -X GET "{{base_url}}/external/v1/reference/postman?base_url=https%3A%2F%2Fyour-host%2Fpersivapps%2Fservice" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
(a file: application/json)

GET /external/v1/reference/pdf #

The API reference as a PDF, with just the calls this token may use.

Any valid token

At most 10 per 10 minutes.

ParameterInTypeRequiredDescription
base_urlquerystringNoYour API base URL, shown in the samples
Sample request
curl -X GET "{{base_url}}/external/v1/reference/pdf?base_url=https%3A%2F%2Fyour-host%2Fpersivapps%2Fservice" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
(a file: application/pdf)

Entries

GET /external/v1/smarttables/{smarttable_id}/entries #

List entries of a SmartTable (paged; search, filters, sort, created/updated time window).

Permission entries:read

ParameterInTypeRequiredDescription
smarttable_idpathintegerYesThe SmartTable id
pagequeryintegerNoPage, from 1
page_sizequeryintegerNo50, 100 (default), 200 or 500
searchquerystringNoMatches any field's value
filtersqueryJSON arrayNoe.g. [{"column_id": "8", "operator": "equals", "value": "Arrived"}]; at most 20
sortquerystringNoA field id, or created_at, updated_at, observation_number, entry_id
dirquerystringNoasc or desc (default)
submittedquerystringNo1 = submitted only, 0 = drafts only
created_afterqueryISO 8601NoCreated at or after (no offset = UTC)
created_beforequeryISO 8601NoCreated before
updated_afterqueryISO 8601NoChanged at or after — use it to fetch what changed since your last run
updated_beforequeryISO 8601NoChanged before
deletedquerystringNo1 lists deleted rows instead (needs entries:delete)
Sample request
curl -X GET "{{base_url}}/external/v1/smarttables/61/entries?page=1&page_size=100&updated_after=2026-10-01T00%3A00%3A00Z&sort=updated_at&dir=asc" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "smarttable_id": 61,
    "entries": [
        {
            "smarttable_id": 61,
            "smarttable_data_id": 189862,
            "smarttable_data_entry_id": 195569,
            "observation_number": 1,
            "values": {
                "1": "Truck MH12AB1234",
                "3": 12.5,
                "8": "Arrived"
            },
            "is_submitted": false,
            "is_deleted": false,
            "created_at": "2026-10-12T09:41:07.512Z",
            "updated_at": "2026-10-12T09:41:07.512Z",
            "created_by": "Acme Logistics (integration)"
        }
    ],
    "page": 1,
    "page_size": 100,
    "total_count": 1,
    "total_pages": 1
}

GET /external/v1/smarttables/data/{smarttable_data_id} #

Read one row (an observation of an entry).

Permission entries:read

ParameterInTypeRequiredDescription
smarttable_data_idpathintegerYesA row (observation) id
Sample request
curl -X GET "{{base_url}}/external/v1/smarttables/data/189862" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "row": {
        "smarttable_id": 61,
        "smarttable_data_id": 189862,
        "smarttable_data_entry_id": 195569,
        "observation_number": 1,
        "values": {
            "1": "Truck MH12AB1234",
            "3": 12.5,
            "8": "Arrived"
        },
        "is_submitted": false,
        "is_deleted": false,
        "created_at": "2026-10-12T09:41:07.512Z",
        "updated_at": "2026-10-12T09:41:07.512Z",
        "created_by": "Acme Logistics (integration)"
    }
}

GET /external/v1/entries/{smarttable_data_entry_id} #

Read an entry with all its observations.

Permission entries:read

ParameterInTypeRequiredDescription
smarttable_data_entry_idpathintegerYesAn entry id
Sample request
curl -X GET "{{base_url}}/external/v1/entries/195569" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "smarttable_id": 61,
    "smarttable_data_entry_id": 195569,
    "observations": [
        {
            "smarttable_id": 61,
            "smarttable_data_id": 189862,
            "smarttable_data_entry_id": 195569,
            "observation_number": 1,
            "values": {
                "1": "Truck MH12AB1234",
                "3": 12.5,
                "8": "Arrived"
            },
            "is_submitted": false,
            "is_deleted": false,
            "created_at": "2026-10-12T09:41:07.512Z",
            "updated_at": "2026-10-12T09:41:07.512Z",
            "created_by": "Acme Logistics (integration)"
        }
    ]
}

DELETE /external/v1/smarttables/data/{smarttable_data_id} #

Delete a row (soft delete; can be restored).

Permission entries:delete

ParameterInTypeRequiredDescription
smarttable_data_idpathintegerYesA row (observation) id
Sample request
curl -X DELETE "{{base_url}}/external/v1/smarttables/data/189862" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "message": "Deleted",
    "smarttable_data_id": 189862
}

POST /external/v1/smarttables/data/{smarttable_data_id}/restore #

Restore a deleted row.

Permission entries:delete

ParameterInTypeRequiredDescription
smarttable_data_idpathintegerYesA row (observation) id
Sample request
curl -X POST "{{base_url}}/external/v1/smarttables/data/189862/restore" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "message": "Restored",
    "smarttable_data_id": 189862
}

DELETE /external/v1/smarttables/data/{smarttable_data_id}/permanent #

Delete a row permanently (cannot be undone).

Permission entries:admin

ParameterInTypeRequiredDescription
smarttable_data_idpathintegerYesA row (observation) id
Sample request
curl -X DELETE "{{base_url}}/external/v1/smarttables/data/189862/permanent" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "message": "Deleted permanently",
    "smarttable_data_id": 189862
}

POST /external/v1/smarttables/data/{smarttable_data_id}/unsubmit #

Unsubmit a submitted row so it can be edited again.

Permission entries:admin

ParameterInTypeRequiredDescription
smarttable_data_idpathintegerYesA row (observation) id
Sample request
curl -X POST "{{base_url}}/external/v1/smarttables/data/189862/unsubmit" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "message": "Unsubmitted",
    "smarttable_data_id": 189862
}

POST /external/v1/smarttables/data/{smarttable_data_id}/release-lock #

Release the edit lock on a row someone left open.

Permission entries:admin

ParameterInTypeRequiredDescription
smarttable_data_idpathintegerYesA row (observation) id
Sample request
curl -X POST "{{base_url}}/external/v1/smarttables/data/189862/release-lock" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "message": "Edit lock released",
    "smarttable_data_id": 189862
}

POST /external/v1/smarttables/data/save #

Create an entry, or a new observation of an entry this client owns.

Permission entries:write

Required fields are checked by the entry form, not by the save: send every field the SmartTable needs.

ParameterInTypeRequiredDescription
smarttable_idbodyintegerYesThe SmartTable to save into
smarttable_valuesbodyobjectYesField values keyed by field id; a table field takes an array
is_submitbodybooleanNotrue submits (and starts any approval workflow); default saves a draft
client_op_idbodystringNoYour unique id for this save; a repeat returns the original row (200, duplicate: true)
smarttable_data_entry_idbodyintegerNoAdd a new observation to one of your entries instead
observation_numberbodyintegerNoWith smarttable_data_entry_id: which observation
attachments__<field_id>formfileNoFiles, when sent as multipart/form-data (smarttable_values as a JSON string)
Sample request
curl -X POST "{{base_url}}/external/v1/smarttables/data/save" \
  -H "Authorization: Bearer {{access_token}}" \
  -H "Content-Type: application/json" \
  -d '{"smarttable_id": 61, "smarttable_values": {"1": "Truck MH12AB1234", "3": 12.5, "8": "Arrived"}, "is_submit": false, "client_op_id": "acme-dispatch-000123"}'
Sample response · 201
{
    "status": "success",
    "message": "Saved",
    "duplicate": false,
    "smarttable_id": 61,
    "smarttable_data_id": 189862,
    "smarttable_data_entry_id": 195569,
    "observation_number": 1
}

PUTPATCHPOST /external/v1/smarttables/data/{smarttable_data_id}/update #

Update fields of a saved row.

Permission entries:write

PUT, PATCH and POST are all accepted. Only rows this client created, unless it may change any entry.

ParameterInTypeRequiredDescription
smarttable_data_idpathintegerYesA row (observation) id
smarttable_valuesbodyobjectYesOnly the fields to change; the rest keep their values
cleared_fieldsbodyarray of stringNoField ids to blank (an empty string means "no change")
cleared_attachmentsbodyarray of stringNoAttachment field ids to empty
is_submitbodybooleanNotrue submits a draft
Sample request
curl -X PUT "{{base_url}}/external/v1/smarttables/data/189862/update" \
  -H "Authorization: Bearer {{access_token}}" \
  -H "Content-Type: application/json" \
  -d '{"smarttable_values": {"8": "Unloaded"}, "cleared_fields": ["3"]}'
Sample response · 200
{
    "status": "success",
    "message": "Updated",
    "smarttable_id": 61,
    "smarttable_data_id": 189862,
    "smarttable_data_entry_id": 195569,
    "observation_number": 1
}

Bulk operations

POST /external/v1/bulk/delete #

Delete many rows (soft delete; can be restored). Body: {"smarttable_data_ids": [...]}.

Permission entries.bulk:delete

ParameterInTypeRequiredDescription
smarttable_data_idsbodyarray of integerYesRow ids, at most 500
Sample request
curl -X POST "{{base_url}}/external/v1/bulk/delete" \
  -H "Authorization: Bearer {{access_token}}" \
  -H "Content-Type: application/json" \
  -d '{"smarttable_data_ids": [189862, 189863, 999999]}'
Sample response · 200
{
    "status": "success",
    "deleted": [
        189862,
        189863
    ],
    "failed": [
        {
            "id": 999999,
            "error": "Not found"
        }
    ]
}

POST /external/v1/bulk/restore #

Restore many deleted rows. Body: {"smarttable_data_ids": [...]}.

Permission entries.bulk:delete

ParameterInTypeRequiredDescription
smarttable_data_idsbodyarray of integerYesRow ids, at most 500
Sample request
curl -X POST "{{base_url}}/external/v1/bulk/restore" \
  -H "Authorization: Bearer {{access_token}}" \
  -H "Content-Type: application/json" \
  -d '{"smarttable_data_ids": [189862, 189863]}'
Sample response · 200
{
    "status": "success",
    "restored": [
        189862,
        189863
    ],
    "failed": []
}

POST /external/v1/bulk/delete-permanently #

Permanently delete many rows (cannot be undone). Body: {"smarttable_data_ids": [...]}.

Permission entries.bulk:admin

ParameterInTypeRequiredDescription
smarttable_data_idsbodyarray of integerYesRow ids, at most 500
Sample request
curl -X POST "{{base_url}}/external/v1/bulk/delete-permanently" \
  -H "Authorization: Bearer {{access_token}}" \
  -H "Content-Type: application/json" \
  -d '{"smarttable_data_ids": [189862]}'
Sample response · 200
{
    "status": "success",
    "deleted": [
        189862
    ],
    "failed": []
}

POST /external/v1/bulk/update-version #

Move many rows to the latest approved schema and/or display version. Body: {"smarttable_data_ids": [...], "update": "entry" | "display" | "both"}.

Permission entries.bulk:admin

ParameterInTypeRequiredDescription
smarttable_data_idsbodyarray of integerYesRow ids, at most 500
updatebodystringNoentry, display or both (default)
Sample request
curl -X POST "{{base_url}}/external/v1/bulk/update-version" \
  -H "Authorization: Bearer {{access_token}}" \
  -H "Content-Type: application/json" \
  -d '{"smarttable_data_ids": [189862], "update": "both"}'
Sample response · 200
{
    "status": "success",
    "updated": [
        {
            "id": 189862,
            "entry_version_id": 412,
            "display_version_id": 233
        }
    ],
    "failed": []
}

POST /external/v1/smarttables/{smarttable_id}/bulk-upload #

Add rows to a relational SmartTable from an Excel file (multipart, key "file"; column headers = field names).

Permission entries.bulk:write

Relational SmartTables only. At most 10 uploads per hour.

ParameterInTypeRequiredDescription
smarttable_idpathintegerYesThe SmartTable id
fileformfileYes.xlsx or .xls, at most 10 MB; first row = field names
Sample request
curl -X POST "{{base_url}}/external/v1/smarttables/64/bulk-upload" \
  -H "Authorization: Bearer {{access_token}}" \
  -F "file=@vehicles.xlsx"
Sample response · 201
{
    "status": "success",
    "smarttable_id": 64,
    "message": "Bulk upload completed",
    "total_rows": 120,
    "added": 118,
    "failed": 2,
    "errors": [
        "Row 14: Vehicle number is required",
        "Row 87: duplicate Vehicle number"
    ]
}

Attestation and eSign

GET /external/v1/smarttables/data/{smarttable_data_id}/attestation #

A row's attestation and eSign status.

Permission entries.attestation:read

attested_by is null while the row is not attested.

ParameterInTypeRequiredDescription
smarttable_data_idpathintegerYesA row (observation) id
Sample request
curl -X GET "{{base_url}}/external/v1/smarttables/data/189862/attestation" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "attestation": {
        "smarttable_data_id": 189862,
        "attested": true,
        "attested_by": {
            "user_id": 431,
            "name": "Acme Logistics (integration)"
        },
        "esign": {
            "status": null,
            "transaction_id": null
        }
    }
}

POST /external/v1/smarttables/data/{smarttable_data_id}/attestation #

Attest a row with a signature image; it is recorded as signed by this integration.

Permission entries.attestation:write

Only a submitted row. Recorded as signed by this integration.

ParameterInTypeRequiredDescription
smarttable_data_idpathintegerYesA row (observation) id
imagebodystring (base64) or fileYesPNG or JPEG signature, at most 1 MB; or multipart field "image"
Sample request
curl -X POST "{{base_url}}/external/v1/smarttables/data/189862/attestation" \
  -H "Authorization: Bearer {{access_token}}" \
  -H "Content-Type: application/json" \
  -d '{"image": "iVBORw0KGgoAAAANSUhEUgAA..."}'
Sample response · 201
{
    "status": "success",
    "attestation": {
        "smarttable_data_id": 189862,
        "attested": true,
        "attested_by": {
            "user_id": 431,
            "name": "Acme Logistics (integration)"
        },
        "esign": {
            "status": null,
            "transaction_id": null
        }
    }
}

DELETE /external/v1/smarttables/data/{smarttable_data_id}/attestation #

Remove a row's attestation.

Permission entries.attestation:delete

ParameterInTypeRequiredDescription
smarttable_data_idpathintegerYesA row (observation) id
Sample request
curl -X DELETE "{{base_url}}/external/v1/smarttables/data/189862/attestation" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "attestation": {
        "smarttable_data_id": 189862,
        "attested": false,
        "attested_by": null,
        "esign": {
            "status": null,
            "transaction_id": null
        }
    }
}

Attachments

GET /external/v1/smarttables/data/{smarttable_data_id}/attachments #

List a row's attachments.

Permission entries.attachments:read

Files are grouped by attachment field id.

ParameterInTypeRequiredDescription
smarttable_data_idpathintegerYesA row (observation) id
Sample request
curl -X GET "{{base_url}}/external/v1/smarttables/data/189862/attachments" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "smarttable_data_id": 189862,
    "attachments": {
        "6": [
            {
                "filename": "0001_3f9a1c2b7d4e_weighbridge-slip.pdf",
                "path": "smarttables/data/189862/attachments/6/0001_3f9a1c2b7d4e_weighbridge-slip.pdf"
            }
        ]
    }
}

GET /external/v1/smarttables/data/{smarttable_data_id}/attachments/{field_id}/{filename} #

Download one attachment.

Permission entries.attachments:read

ParameterInTypeRequiredDescription
smarttable_data_idpathintegerYesA row (observation) id
field_idpathintegerYesThe attachment field id
filenamepathstringYesA filename from the list call
Sample request
curl -X GET "{{base_url}}/external/v1/smarttables/data/189862/attachments/6/0001_3f9a1c2b7d4e_weighbridge-slip.pdf" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
(a file: application/octet-stream)

POST /external/v1/smarttables/data/{smarttable_data_id}/attachments/{field_id} #

Upload files to an attachment field (multipart, key "files").

Permission entries.attachments:write

ParameterInTypeRequiredDescription
smarttable_data_idpathintegerYesA row (observation) id
field_idpathintegerYesThe attachment field id
filesformfileYesOne or more files (repeat the key), at most 20
Sample request
curl -X POST "{{base_url}}/external/v1/smarttables/data/189862/attachments/6" \
  -H "Authorization: Bearer {{access_token}}" \
  -F "files=@weighbridge-slip.pdf"
Sample response · 201
{
    "status": "success",
    "smarttable_data_id": 189862,
    "field_id": "6",
    "files": [
        "0001_3f9a1c2b7d4e_weighbridge-slip.pdf"
    ]
}

DELETE /external/v1/smarttables/data/{smarttable_data_id}/attachments/{field_id}/{filename} #

Remove one attachment.

Permission entries.attachments:delete

ParameterInTypeRequiredDescription
smarttable_data_idpathintegerYesA row (observation) id
field_idpathintegerYesThe attachment field id
filenamepathstringYesThe stored filename
Sample request
curl -X DELETE "{{base_url}}/external/v1/smarttables/data/189862/attachments/6/0001_3f9a1c2b7d4e_weighbridge-slip.pdf" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "message": "Removed",
    "smarttable_data_id": 189862,
    "field_id": "6",
    "filename": "0001_3f9a1c2b7d4e_weighbridge-slip.pdf"
}

Comments and notes

GET /external/v1/smarttables/data/{smarttable_data_id}/comments #

List a row's comments and notes.

Permission entries.comments:read

kind is "note" for notes added in the app, else "comment".

ParameterInTypeRequiredDescription
smarttable_data_idpathintegerYesA row (observation) id
Sample request
curl -X GET "{{base_url}}/external/v1/smarttables/data/189862/comments" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "smarttable_data_id": 189862,
    "comments": [
        {
            "text": "Weighbridge slip attached.",
            "author": "Asha Patel",
            "at": "2026-10-12T10:02:11Z",
            "kind": "comment",
            "attachments": []
        }
    ]
}

POST /external/v1/smarttables/data/{smarttable_data_id}/comments #

Add a comment to a row.

Permission entries.comments:write

ParameterInTypeRequiredDescription
smarttable_data_idpathintegerYesA row (observation) id
textbodystringYesPlain text, at most 5,000 characters
Sample request
curl -X POST "{{base_url}}/external/v1/smarttables/data/189862/comments" \
  -H "Authorization: Bearer {{access_token}}" \
  -H "Content-Type: application/json" \
  -d '{"text": "Unloaded at gate 3."}'
Sample response · 201
{
    "status": "success",
    "message": "Comment added",
    "smarttable_data_id": 189862
}

Approval status

GET /external/v1/entries/{smarttable_data_entry_id}/approval #

An entry's approval status, with earlier rounds.

Permission entries.approvals:read

Read only: approvers act in the app. Signatures are never returned. A SmartTable without a workflow returns just "approval_enabled": false.

ParameterInTypeRequiredDescription
smarttable_data_entry_idpathintegerYesAn entry id
Sample request
curl -X GET "{{base_url}}/external/v1/entries/195569/approval" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "smarttable_data_entry_id": 195569,
    "approval_enabled": true,
    "approval": {
        "approval_workflow_enabled": true,
        "interleaved": false,
        "total_steps": 2,
        "current_step": 2,
        "current_entry_step": null,
        "steps_done": 1,
        "steps_pending": 1,
        "is_fully_approved": false,
        "is_rejected": false,
        "is_published": false,
        "published_at": null,
        "waiting_for_approval": true,
        "approved_at": null,
        "steps": [
            {
                "step": 1,
                "label": "Step 1",
                "mode": "any",
                "approvers": [
                    12,
                    15
                ],
                "approvers_list": [
                    {
                        "name": "Asha Patel"
                    },
                    {
                        "name": "Vikram Rao"
                    }
                ],
                "status": "approved",
                "approved_by": [
                    {
                        "name": "Asha Patel"
                    }
                ],
                "pending_approvers": [],
                "comment": "Looks fine"
            }
        ],
        "publish_notification_users": [],
        "email_publish_notification_users": [],
        "publish_email_settings": {
            "subject": "",
            "body": ""
        }
    }
}

GET /external/v1/entries/{smarttable_data_entry_id}/approval/history #

Earlier approval rounds of an entry (rejected and resubmitted).

Permission entries.approvals:read

ParameterInTypeRequiredDescription
smarttable_data_entry_idpathintegerYesAn entry id
Sample request
curl -X GET "{{base_url}}/external/v1/entries/195569/approval/history" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "smarttable_data_entry_id": 195569,
    "rounds": []
}

Exports

POST /external/v1/bulk/pdf #

Download many entry PDFs as one zip. Body: {"smarttable_data_entry_ids": [...]} (at most 50).

Permission entries.exports:read

Entries you can't read are left out and listed in the X-Skipped-Entry-Ids header. At most 5 per 5 minutes.

ParameterInTypeRequiredDescription
smarttable_data_entry_idsbodyarray of integerYesEntry ids, at most 50
include_attachmentsbodybooleanNoMerge attached PDFs (default true)
Sample request
curl -X POST "{{base_url}}/external/v1/bulk/pdf" \
  -H "Authorization: Bearer {{access_token}}" \
  -H "Content-Type: application/json" \
  -d '{"smarttable_data_entry_ids": [195569, 195570], "include_attachments": false}'
Sample response · 200
(a file: application/zip)

GET /external/v1/entries/{smarttable_data_entry_id}/pdf #

Download an entry's PDF (its display layout).

Permission entries.exports:read

At most 30 PDFs per minute.

ParameterInTypeRequiredDescription
smarttable_data_entry_idpathintegerYesAn entry id
include_attachmentsquerystringNofalse leaves attached PDFs out
Sample request
curl -X GET "{{base_url}}/external/v1/entries/195569/pdf?include_attachments=false" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
(a file: application/pdf)

SmartTables

GET /external/v1/smarttables #

List the SmartTables this client can read.

Permission smarttables:read

Sample request
curl -X GET "{{base_url}}/external/v1/smarttables" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "smarttables": [
        {
            "smarttable_id": 61,
            "name": "Dispatch Log",
            "is_approved": true,
            "is_relational": false,
            "observation_count": 1,
            "version": 4
        }
    ]
}

GET /external/v1/smarttables/{smarttable_id} #

A SmartTable with its current fields (schema).

Permission smarttables:read

ParameterInTypeRequiredDescription
smarttable_idpathintegerYesThe SmartTable id
Sample request
curl -X GET "{{base_url}}/external/v1/smarttables/61" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "smarttable": {
        "smarttable_id": 61,
        "name": "Dispatch Log",
        "is_approved": true,
        "is_relational": false,
        "observation_count": 1,
        "version": 4,
        "fields": [
            {
                "id": 1,
                "name": "Truck number",
                "ui_smarttable_element": "text",
                "optional": false,
                "hide": false,
                "isDisabled": false,
                "show_in_entries_list": true,
                "accordion": "Vehicle",
                "smarttable_section": "Dispatch",
                "row": "",
                "table": false,
                "table_no": "",
                "auto_increment": false,
                "min_value_allowed": "",
                "max_value_allowed": "",
                "acceptance_criteria": "",
                "allowed_data": "",
                "properties": "",
                "hide_for_observation_number": "",
                "autofill_from_observation1": false,
                "attachment": "",
                "attachment_max_count": "",
                "remarks": false,
                "default": ""
            }
        ]
    }
}

GET /external/v1/smarttables/{smarttable_id}/versions #

A SmartTable's schema versions.

Permission smarttables:read

ParameterInTypeRequiredDescription
smarttable_idpathintegerYesThe SmartTable id
Sample request
curl -X GET "{{base_url}}/external/v1/smarttables/61/versions" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "smarttable_id": 61,
    "versions": [
        {
            "version": 4,
            "approved": true,
            "source": "sharepoint",
            "created_at": "2026-10-01T08:00:00Z",
            "field_count": 12
        }
    ]
}

POST /external/v1/smarttables #

Create a SmartTable from a field list (it then waits for approval in the app).

Permission smarttables:write

Needs smarttables:write on all SmartTables.

ParameterInTypeRequiredDescription
namebodystringYesAt most 255 characters
fieldsbodyarrayYesFields with the schema builder's columns
is_relationalbodybooleanNoDefault true
observation_countbodyintegerNo1 to 50
Sample request
curl -X POST "{{base_url}}/external/v1/smarttables" \
  -H "Authorization: Bearer {{access_token}}" \
  -H "Content-Type: application/json" \
  -d '{"name": "Dispatch Log", "is_relational": false, "observation_count": 1, "fields": [{"name": "Truck number", "ui_smarttable_element": "text"}, {"name": "Weight (t)", "ui_smarttable_element": "number", "optional": true}]}'
Sample response · 201
{
    "status": "success",
    "smarttable_id": 88,
    "name": "Dispatch Log",
    "message": "Created; it is usable once approved in the app"
}

PUT /external/v1/smarttables/{smarttable_id}/schema #

Change a SmartTable's fields (creates a new version).

Permission smarttables:write

ParameterInTypeRequiredDescription
smarttable_idpathintegerYesThe SmartTable id
fieldsbodyarrayYesThe full field list; keep existing ids
allow_removebodybooleanNoAllow leaving out existing fields
allow_type_changebodybooleanNoAllow changing a field's type
Sample request
curl -X PUT "{{base_url}}/external/v1/smarttables/61/schema" \
  -H "Authorization: Bearer {{access_token}}" \
  -H "Content-Type: application/json" \
  -d '{"fields": [{"id": 1, "name": "Truck number", "ui_smarttable_element": "text"}, {"name": "Driver phone", "ui_smarttable_element": "text", "optional": true}]}'
Sample response · 200
{
    "status": "success",
    "smarttable_id": 61,
    "version": 5,
    "message": "Saved; a new version may need approval in the app"
}

DELETE /external/v1/smarttables/{smarttable_id} #

Delete a SmartTable.

Permission smarttables:delete

ParameterInTypeRequiredDescription
smarttable_idpathintegerYesThe SmartTable id
Sample request
curl -X DELETE "{{base_url}}/external/v1/smarttables/61" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "message": "Deleted",
    "smarttable_id": 61
}

Display layouts

GET /external/v1/smarttables/{smarttable_id}/display #

A SmartTable's display (print) layout: the latest approved version, or ?version=N.

Permission smarttables.display:read

ParameterInTypeRequiredDescription
smarttable_idpathintegerYesThe SmartTable id
versionqueryintegerNoA version number (default the latest approved)
include_unapprovedquerystringNo1 to include versions not approved yet
Sample request
curl -X GET "{{base_url}}/external/v1/smarttables/61/display?version=3" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "smarttable_id": 61,
    "display": {
        "version": 3,
        "approved": true,
        "created_at": "2026-09-20T07:30:00Z",
        "layout": {
            "cells": [
                {
                    "address": "B2",
                    "row": 1,
                    "column": 1,
                    "value": "<pa_1>",
                    "font": {
                        "bold": true,
                        "name": "Calibri",
                        "size": 11
                    },
                    "column_width": 18,
                    "row_height": 15
                }
            ]
        }
    }
}

GET /external/v1/smarttables/{smarttable_id}/display/versions #

A SmartTable's display layout versions.

Permission smarttables.display:read

ParameterInTypeRequiredDescription
smarttable_idpathintegerYesThe SmartTable id
Sample request
curl -X GET "{{base_url}}/external/v1/smarttables/61/display/versions" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "smarttable_id": 61,
    "versions": [
        {
            "version": 3,
            "approved": true,
            "created_at": "2026-09-20T07:30:00Z"
        }
    ]
}

SharePoint and Google Sheets

GET /external/v1/smarttables/{smarttable_id}/source #

Where a SmartTable's schema comes from (SharePoint, Google Sheets or the schema builder).

Permission smarttables.sources:read

ParameterInTypeRequiredDescription
smarttable_idpathintegerYesThe SmartTable id
Sample request
curl -X GET "{{base_url}}/external/v1/smarttables/61/source" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "smarttable_id": 61,
    "source": "sharepoint",
    "url": "https://contoso.sharepoint.com/sites/ops/Shared%20Documents/dispatch.xlsx",
    "sync_locked": false
}

POST /external/v1/smarttables/{smarttable_id}/sync #

Refresh a SmartTable from its spreadsheet (new versions may need approval in the app).

Permission smarttables.sources:write

At most 20 refreshes and creates per hour.

ParameterInTypeRequiredDescription
smarttable_idpathintegerYesThe SmartTable id
Sample request
curl -X POST "{{base_url}}/external/v1/smarttables/61/sync" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "smarttable_id": 61,
    "source": "sharepoint",
    "versions_updated": true,
    "entry_version": 5,
    "display_version": 3
}

POST /external/v1/smarttables/from-source #

Create a SmartTable from a SharePoint or Google Sheets file (it then waits for approval in the app).

Permission smarttables.sources:write

Needs smarttables.sources:write and smarttables:write, both on all SmartTables.

ParameterInTypeRequiredDescription
namebodystringYesAt most 255 characters
sharepoint_urlbodystringNohttps link on *.sharepoint.com
google_sheets_urlbodystringNohttps link on docs.google.com (instead of sharepoint_url)
is_relationalbodybooleanNoDefault false
observation_countbodyintegerNo1 to 50
Sample request
curl -X POST "{{base_url}}/external/v1/smarttables/from-source" \
  -H "Authorization: Bearer {{access_token}}" \
  -H "Content-Type: application/json" \
  -d '{"name": "Vehicle Master", "sharepoint_url": "https://contoso.sharepoint.com/sites/ops/Shared%20Documents/vehicles.xlsx", "is_relational": true}'
Sample response · 201
{
    "status": "success",
    "smarttable_id": 89,
    "name": "Vehicle Master",
    "message": "Created; it is usable once approved in the app"
}

SmartTable settings

GET /external/v1/smarttables/{smarttable_id}/settings #

A SmartTable's settings.

Permission smarttables.settings:read

ParameterInTypeRequiredDescription
smarttable_idpathintegerYesThe SmartTable id
Sample request
curl -X GET "{{base_url}}/external/v1/smarttables/61/settings" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "smarttable_id": 61,
    "settings": {
        "observation_count": 1,
        "submit_button_label": "Submit",
        "show_observation_number": false,
        "show_min_max_values": true,
        "is_autosave_enabled": true,
        "autosave_enabled_by_default": false,
        "enable_attestation": false,
        "require_submitter_signature": false,
        "enable_notification_comments": false,
        "enable_notes": false,
        "enable_entry_access_control": false,
        "autosave_interval": 60,
        "freeze_columns_fields": [
            1
        ],
        "filter_fields": [
            8
        ],
        "pdf_filename_fields": [
            1
        ],
        "primary_field_id": null,
        "entries_page_size": 100,
        "show_entries": "all",
        "show_entries_attribute_id": null,
        "submit_admin_users": [],
        "unsubmit_admin_users": [],
        "attest_users": [],
        "submit_notification_users": [],
        "offline_enabled": false,
        "compress_attachments": true
    }
}

PATCH /external/v1/smarttables/{smarttable_id}/settings #

Change some of a SmartTable's settings (only the keys sent).

Permission smarttables.settings:write

Saves a new settings version and returns all settings.

ParameterInTypeRequiredDescription
smarttable_idpathintegerYesThe SmartTable id
<setting>bodyanyYesOnly the settings to change (see the guide for the list)
Sample request
curl -X PATCH "{{base_url}}/external/v1/smarttables/61/settings" \
  -H "Authorization: Bearer {{access_token}}" \
  -H "Content-Type: application/json" \
  -d '{"submit_button_label": "Send", "autosave_interval": 30}'
Sample response · 200
{
    "status": "success",
    "smarttable_id": 61,
    "version": 7,
    "settings": {
        "observation_count": 1,
        "submit_button_label": "Send",
        "show_observation_number": false,
        "show_min_max_values": true,
        "is_autosave_enabled": true,
        "autosave_enabled_by_default": false,
        "enable_attestation": false,
        "require_submitter_signature": false,
        "enable_notification_comments": false,
        "enable_notes": false,
        "enable_entry_access_control": false,
        "autosave_interval": 30,
        "freeze_columns_fields": [
            1
        ],
        "filter_fields": [
            8
        ],
        "pdf_filename_fields": [
            1
        ],
        "primary_field_id": null,
        "entries_page_size": 100,
        "show_entries": "all",
        "show_entries_attribute_id": null,
        "submit_admin_users": [],
        "unsubmit_admin_users": [],
        "attest_users": [],
        "submit_notification_users": [],
        "offline_enabled": false,
        "compress_attachments": true
    }
}

Approval workflow

GET /external/v1/smarttables/{smarttable_id}/approval-workflow #

A SmartTable's approval workflow.

Permission smarttables.approval:read

ParameterInTypeRequiredDescription
smarttable_idpathintegerYesThe SmartTable id
Sample request
curl -X GET "{{base_url}}/external/v1/smarttables/61/approval-workflow" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "smarttable_id": 61,
    "approval_workflow": {
        "enabled": true,
        "interleaved": false,
        "step0": {
            "enabled": false,
            "notify_on_later_steps": false,
            "deadline_days": 0
        },
        "steps": [
            {
                "step": 1,
                "mode": "any",
                "approvers": [
                    12,
                    15
                ],
                "notify_on_later_steps": false,
                "deadline_days": 2
            }
        ],
        "on_publish": {
            "notify_users": [],
            "email_users": [
                30
            ],
            "email": {
                "subject": "Approved",
                "body": "Please find the PDF attached.",
                "signature": ""
            }
        }
    }
}

PUT /external/v1/smarttables/{smarttable_id}/approval-workflow #

Replace a SmartTable's approval workflow (applies to entries created afterwards).

Permission smarttables.approval:write

ParameterInTypeRequiredDescription
smarttable_idpathintegerYesThe SmartTable id
enabledbodybooleanYesfalse turns approvals off
stepsbodyarrayNo1 to 20 steps: mode any|all, approvers (user ids), deadline_days
step0bodyobjectNoenabled, notify_on_later_steps, deadline_days
on_publishbodyobjectNonotify_users, email_users, email {subject, body, signature}
Sample request
curl -X PUT "{{base_url}}/external/v1/smarttables/61/approval-workflow" \
  -H "Authorization: Bearer {{access_token}}" \
  -H "Content-Type: application/json" \
  -d '{"enabled": true, "steps": [{"mode": "any", "approvers": [12, 15], "deadline_days": 2}, {"mode": "all", "approvers": [20]}]}'
Sample response · 200
{
    "status": "success",
    "smarttable_id": 61,
    "version": 6,
    "approval_workflow": {
        "enabled": true,
        "interleaved": false,
        "step0": {
            "enabled": false,
            "notify_on_later_steps": false,
            "deadline_days": 0
        },
        "steps": [
            {
                "step": 1,
                "mode": "any",
                "approvers": [
                    12,
                    15
                ],
                "notify_on_later_steps": false,
                "deadline_days": 2
            }
        ],
        "on_publish": {
            "notify_users": [],
            "email_users": [
                30
            ],
            "email": {
                "subject": "Approved",
                "body": "Please find the PDF attached.",
                "signature": ""
            }
        }
    }
}

SmartTable Steps

GET /external/v1/smarttables/{smarttable_id}/steps #

A SmartTable's steps and who is assigned to each.

Permission smarttables.steps:read

ParameterInTypeRequiredDescription
smarttable_idpathintegerYesThe SmartTable id
Sample request
curl -X GET "{{base_url}}/external/v1/smarttables/61/steps" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "smarttable_id": 61,
    "steps": [
        {
            "step_number": 1,
            "step_name": "Gate in",
            "sheet_step_name": "Gate in",
            "field_count": 6,
            "assignees": {
                "users": [
                    12
                ]
            },
            "resolved_users": [
                {
                    "id": 12,
                    "name": "Asha Patel"
                }
            ],
            "show_to_later_steps": false
        }
    ]
}

PUT /external/v1/smarttables/{smarttable_id}/steps #

Set who is assigned to each step (steps left out are deactivated).

Permission smarttables.steps:write

ParameterInTypeRequiredDescription
smarttable_idpathintegerYesThe SmartTable id
stepsbodyarrayYesstep_number + assignees {users, attributes}; left out = deactivated
Sample request
curl -X PUT "{{base_url}}/external/v1/smarttables/61/steps" \
  -H "Authorization: Bearer {{access_token}}" \
  -H "Content-Type: application/json" \
  -d '{"steps": [{"step_number": 1, "assignees": {"users": [12]}}]}'
Sample response · 200
{
    "status": "success",
    "smarttable_id": 61,
    "steps": [
        {
            "step_number": 1,
            "step_name": "Gate in",
            "sheet_step_name": "Gate in",
            "field_count": 6,
            "assignees": {
                "users": [
                    12
                ]
            },
            "resolved_users": [
                {
                    "id": 12,
                    "name": "Asha Patel"
                }
            ],
            "show_to_later_steps": false
        }
    ]
}

Access

GET /external/v1/smarttables/{smarttable_id}/access #

Who has access to a SmartTable, and with which role.

Permission access:read

ParameterInTypeRequiredDescription
smarttable_idpathintegerYesThe SmartTable id
Sample request
curl -X GET "{{base_url}}/external/v1/smarttables/61/access" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "smarttable_id": 61,
    "access": [
        {
            "user_id": 12,
            "name": "Asha Patel",
            "username": "asha.patel",
            "role": "admin",
            "is_api_client": false
        }
    ]
}

POST /external/v1/smarttables/{smarttable_id}/access #

Give users access to a SmartTable (role: admin, user or viewer).

Permission access:admin

ParameterInTypeRequiredDescription
smarttable_idpathintegerYesThe SmartTable id
user_idsbodyarray of integerYesActive users of your organization
rolebodystringNoadmin, user (default) or viewer
Sample request
curl -X POST "{{base_url}}/external/v1/smarttables/61/access" \
  -H "Authorization: Bearer {{access_token}}" \
  -H "Content-Type: application/json" \
  -d '{"user_ids": [12, 15], "role": "user"}'
Sample response · 201
{
    "status": "success",
    "smarttable_id": 61,
    "granted": [
        15
    ],
    "already_had_access": [
        12
    ],
    "role": "user"
}

PUT /external/v1/smarttables/{smarttable_id}/access/{user_id} #

Change a user's role on a SmartTable.

Permission access:admin

ParameterInTypeRequiredDescription
smarttable_idpathintegerYesThe SmartTable id
user_idpathintegerYesA user id of your organization
rolebodystringYesadmin, user or viewer
forcebodybooleanNoGo ahead although the user holds an in-progress step
Sample request
curl -X PUT "{{base_url}}/external/v1/smarttables/61/access/15" \
  -H "Authorization: Bearer {{access_token}}" \
  -H "Content-Type: application/json" \
  -d '{"role": "viewer"}'
Sample response · 200
{
    "status": "success",
    "smarttable_id": 61,
    "user_id": 15,
    "role": "viewer"
}

DELETE /external/v1/smarttables/{smarttable_id}/access/{user_id} #

Remove a user's access to a SmartTable.

Permission access:admin

ParameterInTypeRequiredDescription
smarttable_idpathintegerYesThe SmartTable id
user_idpathintegerYesA user id of your organization
forcequerystringNotrue: remove although the user holds an in-progress step
Sample request
curl -X DELETE "{{base_url}}/external/v1/smarttables/61/access/15" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "message": "Access removed",
    "smarttable_id": 61,
    "user_id": 15
}

GET /external/v1/access/users #

The organization's users (id, name, username), to give access to.

Permission access:admin

ParameterInTypeRequiredDescription
searchquerystringNoName or username
pagequeryintegerNoFrom 1
Sample request
curl -X GET "{{base_url}}/external/v1/access/users?search=asha" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "users": [
        {
            "user_id": 12,
            "name": "Asha Patel",
            "username": "asha.patel"
        }
    ],
    "page": 1,
    "page_size": 100,
    "total_count": 1
}

GET /external/v1/smarttables/{smarttable_id}/entry-access #

A SmartTable's entry access rules (who can view, edit and administer its rows).

Permission access:read

ParameterInTypeRequiredDescription
smarttable_idpathintegerYesThe SmartTable id
Sample request
curl -X GET "{{base_url}}/external/v1/smarttables/61/entry-access" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "smarttable_id": 61,
    "feature_enabled_on_server": true,
    "enabled": true,
    "rules": {
        "view": {
            "match_creator_attribute": "5"
        },
        "edit": {
            "creator_only": true
        },
        "admin": []
    },
    "show_entries": "all"
}

PUT /external/v1/smarttables/{smarttable_id}/entry-access #

Set a SmartTable's entry access rules and switch them on or off (new version).

Permission access:admin

ParameterInTypeRequiredDescription
smarttable_idpathintegerYesThe SmartTable id
enabledbodybooleanNoSwitch entry access control on or off
rulesbodyobjectNoview / edit / admin: users, attributes, creator_only, match_creator_attribute, or null
Sample request
curl -X PUT "{{base_url}}/external/v1/smarttables/61/entry-access" \
  -H "Authorization: Bearer {{access_token}}" \
  -H "Content-Type: application/json" \
  -d '{"enabled": true, "rules": {"view": {"match_creator_attribute": "5"}, "edit": {"creator_only": true, "users": [12]}}}'
Sample response · 200
{
    "status": "success",
    "smarttable_id": 61,
    "feature_enabled_on_server": true,
    "enabled": true,
    "rules": {
        "view": {
            "match_creator_attribute": "5"
        },
        "edit": {
            "creator_only": true,
            "users": [
                12
            ]
        },
        "admin": []
    },
    "show_entries": "all"
}

PATCH /external/v1/smarttables/data/{smarttable_data_id}/access #

Set who can view, edit or administer one row (overrides the SmartTable default).

Permission access:admin

ParameterInTypeRequiredDescription
smarttable_data_idpathintegerYesA row (observation) id
view | edit | adminbodyobject or nullYes{"users": [...], "attributes": {attr id: [values]}}; null = back to the SmartTable default
Sample request
curl -X PATCH "{{base_url}}/external/v1/smarttables/data/189862/access" \
  -H "Authorization: Bearer {{access_token}}" \
  -H "Content-Type: application/json" \
  -d '{"view": {"users": [12]}, "edit": null}'
Sample response · 200
{
    "status": "success",
    "smarttable_data_id": 189862,
    "access": {
        "view": {
            "users": [
                12
            ]
        }
    }
}

Folders

GET /external/v1/folders #

List folders.

Permission folders:read

Sample request
curl -X GET "{{base_url}}/external/v1/folders" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "folders": [
        {
            "folder_id": 7,
            "name": "Dispatch",
            "smarttable_ids": [
                61,
                62
            ],
            "shared_with": [
                {
                    "user_id": 12,
                    "name": "Asha Patel"
                }
            ]
        }
    ]
}

POST /external/v1/folders #

Create a folder (needs folders:write on all folders).

Permission folders:write

Needs folders:write on all folders.

ParameterInTypeRequiredDescription
namebodystringYesAt most 255 characters
Sample request
curl -X POST "{{base_url}}/external/v1/folders" \
  -H "Authorization: Bearer {{access_token}}" \
  -H "Content-Type: application/json" \
  -d '{"name": "Dispatch"}'
Sample response · 201
{
    "status": "success",
    "folder": {
        "folder_id": 7,
        "name": "Dispatch",
        "smarttable_ids": [],
        "shared_with": []
    }
}

PATCH /external/v1/folders/{folder_id} #

Rename a folder.

Permission folders:write

ParameterInTypeRequiredDescription
folder_idpathintegerYesA folder id
namebodystringYesAt most 255 characters
Sample request
curl -X PATCH "{{base_url}}/external/v1/folders/7" \
  -H "Authorization: Bearer {{access_token}}" \
  -H "Content-Type: application/json" \
  -d '{"name": "Dispatch (North)"}'
Sample response · 200
{
    "status": "success",
    "folder": {
        "folder_id": 7,
        "name": "Dispatch (North)",
        "smarttable_ids": [
            61,
            62
        ],
        "shared_with": [
            {
                "user_id": 12,
                "name": "Asha Patel"
            }
        ]
    }
}

PUT /external/v1/folders/{folder_id}/smarttables #

Set a folder's SmartTables (a SmartTable is in at most one folder).

Permission folders:write

ParameterInTypeRequiredDescription
folder_idpathintegerYesA folder id
smarttable_idsbodyarray of integerYesThe folder's SmartTables, at most 500
Sample request
curl -X PUT "{{base_url}}/external/v1/folders/7/smarttables" \
  -H "Authorization: Bearer {{access_token}}" \
  -H "Content-Type: application/json" \
  -d '{"smarttable_ids": [61, 62]}'
Sample response · 200
{
    "status": "success",
    "folder": {
        "folder_id": 7,
        "name": "Dispatch",
        "smarttable_ids": [
            61,
            62
        ],
        "shared_with": [
            {
                "user_id": 12,
                "name": "Asha Patel"
            }
        ]
    }
}

DELETE /external/v1/folders/{folder_id} #

Delete a folder (its SmartTables are not deleted).

Permission folders:delete

ParameterInTypeRequiredDescription
folder_idpathintegerYesA folder id
Sample request
curl -X DELETE "{{base_url}}/external/v1/folders/7" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "message": "Deleted",
    "folder_id": 7
}

POST /external/v1/folders/{folder_id}/share #

Share a folder: users get the 'user' role on its SmartTables.

Permission folders:admin

ParameterInTypeRequiredDescription
folder_idpathintegerYesA folder id
user_idsbodyarray of integerYesActive users of your organization
Sample request
curl -X POST "{{base_url}}/external/v1/folders/7/share" \
  -H "Authorization: Bearer {{access_token}}" \
  -H "Content-Type: application/json" \
  -d '{"user_ids": [12]}'
Sample response · 200
{
    "status": "success",
    "folder": {
        "folder_id": 7,
        "name": "Dispatch",
        "smarttable_ids": [
            61,
            62
        ],
        "shared_with": [
            {
                "user_id": 12,
                "name": "Asha Patel"
            }
        ]
    }
}

DELETE /external/v1/folders/{folder_id}/share/{user_id} #

Unshare a folder: removes the user's 'user' role on its SmartTables.

Permission folders:admin

ParameterInTypeRequiredDescription
folder_idpathintegerYesA folder id
user_idpathintegerYesA user id of your organization
Sample request
curl -X DELETE "{{base_url}}/external/v1/folders/7/share/12" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "folder": {
        "folder_id": 7,
        "name": "Dispatch",
        "smarttable_ids": [
            61,
            62
        ],
        "shared_with": []
    }
}

History

GET /external/v1/smarttables/{smarttable_id}/history #

A SmartTable's history: versions, approvals, settings changes, bulk uploads.

Permission audit:read

Times are in IST (the app's display time).

ParameterInTypeRequiredDescription
smarttable_idpathintegerYesThe SmartTable id
Sample request
curl -X GET "{{base_url}}/external/v1/smarttables/61/history" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "smarttable_id": 61,
    "history": [
        {
            "event": "ENTRY_VERSION_CREATED",
            "action": "ENTRY_VERSION_CREATED",
            "timestamp": "2026-10-01 13:30:00",
            "performed_by": "Asha Patel",
            "details": {
                "version": 5,
                "approved": 1
            }
        }
    ]
}

GET /external/v1/smarttables/{smarttable_id}/settings-log #

A SmartTable's settings changes (latest 100).

Permission audit:read

ParameterInTypeRequiredDescription
smarttable_idpathintegerYesThe SmartTable id
Sample request
curl -X GET "{{base_url}}/external/v1/smarttables/61/settings-log" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "smarttable_id": 61,
    "changes": [
        {
            "id": 85235,
            "event": "SMART_TABLE_SETTINGS_UPDATED",
            "timestamp": "2026-10-02 11:42:00",
            "user_name": "Asha Patel",
            "details": {
                "autosave_interval": "30",
                "submit_button_label": "Send"
            }
        }
    ]
}

GET /external/v1/smarttables/data/{smarttable_data_id}/history #

A row's history: saves, submits, approvals, deletes.

Permission audit:read

ParameterInTypeRequiredDescription
smarttable_data_idpathintegerYesA row (observation) id
Sample request
curl -X GET "{{base_url}}/external/v1/smarttables/data/189862/history" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "smarttable_data_id": 189862,
    "history": [
        {
            "event": "SMART_TABLE_DATA_SAVED",
            "timestamp": "2026-10-12 15:11:07",
            "performed_by": "Acme Logistics (integration)",
            "details": []
        }
    ]
}

Users (read only)

GET /external/v1/users #

List the organization's users (paged; search, role, active, attribute filters).

Permission users:read

ParameterInTypeRequiredDescription
searchquerystringNoName, username or email
rolequerystringNoA role name
activequerystringNo1 active, 0 inactive
attribute_idqueryintegerNoUsers with this attribute set
attribute_valuequerystringNoWith attribute_id: that exact value
pagequeryintegerNoFrom 1; 100 per page
Sample request
curl -X GET "{{base_url}}/external/v1/users?search=asha&active=1" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "users": [
        {
            "user_id": 12,
            "name": "Asha Patel",
            "username": "asha.patel",
            "email": "asha.patel@example.com",
            "role": "User",
            "active": true,
            "mfa_enabled": false,
            "attributes": [
                {
                    "attribute_id": 5,
                    "name": "Department",
                    "value": "Logistics"
                }
            ],
            "created_at": "2026-03-02T05:10:00Z",
            "updated_at": "2026-09-28T11:02:43Z"
        }
    ],
    "page": 1,
    "page_size": 100,
    "total_count": 1,
    "total_pages": 1
}

GET /external/v1/users/{user_id} #

One user.

Permission users:read

ParameterInTypeRequiredDescription
user_idpathintegerYesA user id of your organization
Sample request
curl -X GET "{{base_url}}/external/v1/users/12" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "user": {
        "user_id": 12,
        "name": "Asha Patel",
        "username": "asha.patel",
        "email": "asha.patel@example.com",
        "role": "User",
        "active": true,
        "mfa_enabled": false,
        "attributes": [
            {
                "attribute_id": 5,
                "name": "Department",
                "value": "Logistics"
            }
        ],
        "created_at": "2026-03-02T05:10:00Z",
        "updated_at": "2026-09-28T11:02:43Z"
    }
}

GET /external/v1/users/{user_id}/smarttables #

The SmartTables a user has been given access to, and their role on each (only SmartTables this client may read the access of).

Permission users:read

Lists only SmartTables your access:read covers.

ParameterInTypeRequiredDescription
user_idpathintegerYesA user id of your organization
Sample request
curl -X GET "{{base_url}}/external/v1/users/12/smarttables" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "user_id": 12,
    "smarttables": [
        {
            "smarttable_id": 61,
            "name": "Dispatch Log",
            "role": "admin"
        }
    ]
}

GET /external/v1/roles #

The organization's roles.

Permission users:read

Sample request
curl -X GET "{{base_url}}/external/v1/roles" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "roles": [
        {
            "role_id": 3,
            "name": "User"
        }
    ]
}

GET /external/v1/user-attributes #

The organization's user attributes (definitions).

Permission users:read

Sample request
curl -X GET "{{base_url}}/external/v1/user-attributes" \
  -H "Authorization: Bearer {{access_token}}"
Sample response · 200
{
    "status": "success",
    "attributes": [
        {
            "attribute_id": 5,
            "name": "Department",
            "type": "TEXT_INPUT",
            "default_value": null,
            "is_filter": true
        }
    ]
}

Errors

Errors come back as {"status": "error", "error": "…", "code": "…"}. Program against code.

StatuscodeMeaning
400invalid_requestA value is missing or wrong; the message says which.
400rejectedThe SmartTable's own rules refused it (validation, adding off, submitted and locked).
401—Missing, expired or invalid token.
403permission_deniedThe token lacks the permission; details.required_permission names it.
404not_foundDoesn't exist, isn't covered by your permission, or (for changes) isn't yours.
409conflict codesnot_approved, not_deleted, not_submitted, steps_pending, sync_locked, …
429rate_limitedToo many requests; wait and retry.

Generated 2026-10-12 from the API itself; every sample is checked against the live API before release.