Reading and writing rows

Create, read, update and delete rows over REST, with references by uid and the errors you will meet.

The examples on this page use the example app's invoices table, which references vendors and purchase_orders. Every request needs your app's uid (on the app's settings page) and a bearer token for one of your app's users or an agent API key.

export AUTHORIC_URL="https://api.authoric.ai"   # your API's address
export APP_UID="<your app uid>"
export TOKEN="<an access token>"

Create a row

Send the columns as JSON. A reference is sent by its _uid name, holding the referenced row's uid: vendor_uid, not vendor_id.

curl -X POST "$AUTHORIC_URL/$APP_UID/autocrud/invoices" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 5f4e9c2a-1b3d-4a8e-9c7f-2d1e6b0a4c88" \
  -d '{
    "invoice_number": "MS-88461",
    "vendor_uid": "0199b8e1-9c04-7a8b-b2d3-6e5f1c8a4d07",
    "purchase_order_uid": "0199b8e2-4b7e-7f10-a6c9-2d8e3f5b1a64",
    "amount": 8760.00,
    "memo": "Sterile kits"
  }'

The response is the new row's uid followed by the fields you sent:

{
  "uid": "0199b8e4-2a6f-7c31-8d5e-41f0a9c37b12",
  "invoice_number": "MS-88461",
  "vendor_uid": "0199b8e1-9c04-7a8b-b2d3-6e5f1c8a4d07",
  "purchase_order_uid": "0199b8e2-4b7e-7f10-a6c9-2d8e3f5b1a64",
  "amount": 8760,
  "memo": "Sterile kits"
}

Columns the database fills in (status defaulting to received, currency to USD, the timestamps) come back when you read the row.

You never send org_id. The row belongs to the caller's organization, or to no organization in a shared table.

Read one row

curl "$AUTHORIC_URL/$APP_UID/autocrud/invoices/0199b8e4-2a6f-7c31-8d5e-41f0a9c37b12" \
  -H "Authorization: Bearer $TOKEN"
{
  "uid": "0199b8e4-2a6f-7c31-8d5e-41f0a9c37b12",
  "invoice_number": "MS-88461",
  "vendor_uid": "0199b8e1-9c04-7a8b-b2d3-6e5f1c8a4d07",
  "purchase_order_uid": "0199b8e2-4b7e-7f10-a6c9-2d8e3f5b1a64",
  "amount": "8760.00",
  "currency": "USD",
  "status": "received",
  "received_at": null,
  "due_at": null,
  "memo": "Sterile kits",
  "spend_category": null,
  "created_at": "2026-10-23T14:02:11.000Z",
  "updated_at": "2026-10-23T14:02:11.000Z",
  "deleted_at": null
}

Two things to notice. DECIMAL columns such as amount come back as strings, which keeps their exact precision; parse them with a decimal type, not a float. And a column marked sensitive comes back as "[redacted]", on this read and on every list and event.

A uid that names no row, a row in another organization, a soft-deleted row and a value that is not a uid at all are the same 404. The API does not say which.

Update a row

PATCH takes only the fields you are changing. Every column is optional on an update.

curl -X PATCH "$AUTHORIC_URL/$APP_UID/autocrud/invoices/0199b8e4-2a6f-7c31-8d5e-41f0a9c37b12" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "status": "matched" }'

The response is the row as it now stands, re-read through the same governed path as a GET, so redacted columns stay redacted. To re-point a reference, send its _uid name: { "purchase_order_uid": "<another purchase order's uid>" }. To clear a nullable reference, send null.

Delete a row

curl -X DELETE "$AUTHORIC_URL/$APP_UID/autocrud/invoices/0199b8e4-2a6f-7c31-8d5e-41f0a9c37b12" \
  -H "Authorization: Bearer $TOKEN"

On a soft-delete table (the example app's invoices is one) the row gets a deleted_at and disappears from every read, update and subscription; it is not purged. On a hard-delete table it is removed.

A row still referenced under a Restrict or No action foreign key cannot be deleted. Delete or re-point the referencing rows first; the error says where they are:

{
  "error": {
    "error": "Cannot delete row because related records exist",
    "code": "DELETE_BLOCKED_BY_DEPENDENCIES",
    "details": {
      "tableName": "invoices",
      "uid": "0199b8e4-2a6f-7c31-8d5e-41f0a9c37b12",
      "blockers": [
        { "childTable": "payments", "deleteRule": "RESTRICT", "count": 2 }
      ]
    },
    "requestId": "3f9a1c7e-2b54-4d8a-9f10-6c2e7b5a4d31"
  },
  "requestId": "3f9a1c7e-2b54-4d8a-9f10-6c2e7b5a4d31"
}

Retry-safe writes

Networks fail after the server has done the work. Send an Idempotency-Key header with a fresh UUID per logical operation on any POST, PATCH or DELETE, and reuse it on the retry:

On retryResult
Same key, same bodyThe stored response, for 24 hours. No second row.
Same key, different body409 IDEMPOTENCY_KEY_REUSED: the key was used for a different request.
Same key, first request still running409 IDEMPOTENCY_IN_PROGRESS: wait, then retry with the same key.
New key, or no keyA normal write.

A key belongs to whoever sent it: the same UUID from another user, another org or another API key is a different key, and never returns your response. It is also scoped to its method and route, so the same UUID on two tables never collides. Policy and validation run before the key is recorded, so a refused request never uses up a key.

Errors

Every failure has the same envelope. Branch on error.code, never on the message:

{
  "error": {
    "error": "'vendor_id' holds an internal id. Use 'vendor_uid' (the uid of a 'vendors' row) instead.",
    "code": "ROW_FIELD_NOT_ACCEPTED",
    "details": { "field": "vendor_id", "use": "vendor_uid" },
    "requestId": "3f9a1c7e-2b54-4d8a-9f10-6c2e7b5a4d31"
  },
  "requestId": "3f9a1c7e-2b54-4d8a-9f10-6c2e7b5a4d31"
}
Statuserror.codeWhen
400ROW_FIELD_NOT_ACCEPTEDThe body named id, uid, org_id, an actor column or a raw foreign key. details.use is the field to send instead, when there is one.
400ROW_REFERENCE_NOT_FOUNDA _uid reference names no row in the table it points at. details.field and details.references say which.
400CREATE_REQUIRED_FIELD_MISSINGA column the table requires was absent.
400ROW_VALUE_INVALIDA value does not fit its column: too long, out of range, the wrong type, or not one of an enum's options (listed). details.column names it.
400VALIDATION_ERRORThe body failed the table's schema: an unknown column, a wrong type, a missing required column. details.field names the field, and the message says why.
403nonePolicy does not grant this operation on this table.
404noneNo row with that uid that you can see.
409CREATE_DUPLICATEA unique column already holds that value.
409DELETE_BLOCKED_BY_DEPENDENCIESOther rows still reference this one.
409ROW_EDGE_COLLISIONThe table cannot be served until a column is renamed (see Database).
409noneAn Idempotency-Key conflict.
423TABLE_LOCKEDThe operation is switched off for this table.

A body that fails the table's schema is refused. Validate your payload against the table's schema in the API reference before you send it.

The full list of codes is in the error-code reference.

Next

On this page