Datasets.

A dataset is a named collection of records — a catalogue, a price list, a transaction log. It belongs to the project the key was made on, and it is addressed by its key.

Working — v1 at api.ardaform.net

About datasets

  • Most datasets come from your app's manifest.json, created when a commit is packed. Records sent before then have nowhere to go: 404 NO_DATASET. Create one here for data only your own systems use.
  • Addressed by key, the name your app's code uses — /v1/datasets/products. The key never changes. outbox is reserved.
  • partition_field names the record field that splits the dataset — a branch, a site, a till. A device set to a data partition syncs only the records in it, and you filter by the field's own name (?branch=oldbury). A record may be in several partitions: the field holds a list.
  • access is what the app on the device may do: ro is read-only there. It does not limit this API — a read-only catalogue still has to be filled.
  • The manifest can limit this API: a dataset declared with "api": "ro" answers writes with 403 API_READ_ONLY, and "api": "none" closes every records call with 403 API_CLOSED, whatever the key may do.
  • The package's max_datasets counts every dataset of the account; creating past it is 403 LIMIT_REACHED.

GET/v1/datasetsEvery dataset on the key's project

Every dataset of the key's project, by key, with what each holds now. records and bytes are counted when you ask; cursor is where a sync would start if it started now.

Key scope datasets:r — read datasets

Answer 200 The list

FieldType
datasetsarray of Dataset

Errors

StatusWordWhen
403OUT_OF_SCOPEThe key's scopes do not allow this area and verb (or this dataset, for records). A key edited before an area existed has no entry for it and is refused there until it is edited again in the portal.
404NO_SPACENo space with that id or slug in the key's project — in /v1/spaces/{space}, a space field or parameter, or the Arda-Space header. unassigned is no space (the devices in none). Another project's space answers the same, so nothing can be probed for.

And, like every keyed call: NO_KEY BAD_KEY KEY_REVOKED KEY_EXPIRED ACCOUNT_INACTIVE NO_API_ACCESS RATE_LIMITED INTERNAL.

curl "https://api.ardaform.net/v1/datasets" \
  -H "Authorization: Bearer $ARDA_KEY"
Answer · 200 OKran live 2026-10-27 · test project
{
  "datasets": [
    {
      "key": "zz-docs-products",
      "name": "Docs products",
      "description": "Prices, per branch",
      "access": "rw",
      "partition_field": "branch",
      "retention_mode": "never",
      "retention_value": null,
      "storage_limit_bytes": null,
      "cursor": 0,
      "records": 0,
      "bytes": 0,
      "updated_at": null,
      "created_at": "2026-10-27T20:10:00.663Z"
    }
  ]
}

POST/v1/datasetsCreate a dataset

Makes a dataset on the key's project. Only name is required; without a key one is made from the name.

  • Choose partition_field now if devices will read only their part — changing it later re-lifts every record (below).
  • name longer than 100 characters is cut, and an access other than ro is stored as rw — neither is refused.

Key scope datasets:a — add datasets

Body

FieldType
name requiredstring
at least 1 character
For people. Trimmed; longer than 100 characters is CUT to 100, not refused.
descriptionstringWhat it is for. Trimmed; empty clears it.
accessstring — rw or roWhat the app ON THE DEVICE may do. ro is read-only there; any other value is stored as rw. Does not restrict this API.
partition_fieldstringThe record field that splits the dataset, and the query parameter you filter on. Empty clears it. ⚠️ Changing it on PATCH re-lifts every record and moves every one above your sync cursor.
retention_modestring — never or days or spacenever (the default) keeps everything and refuses writes once full; days drops records older than retention_value days on each write; space drops the oldest once retention_value per cent of storage_limit_bytes is used.
retention_valueinteger
≥ 1
Days for days (≥ 1), per cent for space (1–100). Ignored and stored as null for never. On PATCH, the mode and the value are validated as a pair — the one not sent is read from the dataset.
storage_limit_bytesinteger
≥ 1
A ceiling on this dataset alone, inside the account allowance. Anything but a positive number is stored as null: no ceiling of its own.
keystring
pattern ^[a-z0-9][a-z0-9_-]{0,79}$
Derived from the name when absent (lower-cased, anything else becomes -). outbox is reserved.

Answer 201 Created

FieldType
datasetDatasetA named collection of records — a catalogue, a price list, a transaction log. It belongs to the project the key was made on.

Errors

StatusWordWhen
400BAD_REQUESTThe body is missing a required field, has one of the wrong type or length, or (on PUT /v1/content/…) arrived shorter or longer than its Content-Length. The message says which.
400BAD_RETENTIONretention_mode is not never, days or space, or its retention_value is missing or out of range (days ≥ 1; space 1–100).
400BAD_KEY_FORMATA dataset key or webhook slug is not lower-case letters, digits, - and _, starting with a letter or digit, at most 80 — or is the reserved verify (webhooks).
400RESERVED_KEYoutbox is the device runtime's own queue and cannot be a dataset key.
403LIMIT_REACHEDThe package's ceiling on datasets (max_datasets), webhooks (webhook_endpoints), spaces beyond each project's Default (max_spaces) or devices (max_devices), each across the whole account, is reached.
403OUT_OF_SCOPEThe key's scopes do not allow this area and verb (or this dataset, for records). A key edited before an area existed has no entry for it and is refused there until it is edited again in the portal.
404NO_SPACENo space with that id or slug in the key's project — in /v1/spaces/{space}, a space field or parameter, or the Arda-Space header. unassigned is no space (the devices in none). Another project's space answers the same, so nothing can be probed for.
409DATASET_EXISTSA dataset with that key already exists on the project.
409SPACE_DEFINEDInside a private space (Arda-Space), a dataset cannot be made, changed or dropped: its datasets are the project's, mirrored. Do it without the header and every private space follows; its records are its own.

And, like every keyed call: NO_KEY BAD_KEY KEY_REVOKED KEY_EXPIRED ACCOUNT_INACTIVE NO_API_ACCESS RATE_LIMITED INTERNAL BAD_JSON TOO_LARGE.

curl -X POST "https://api.ardaform.net/v1/datasets" \
  -H "Authorization: Bearer $ARDA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Docs products","key":"zz-docs-products","description":"Prices, per branch","partition_field":"branch"}'
Answer · 201 Createdran live 2026-10-27 · test project
{
  "dataset": {
    "key": "zz-docs-products",
    "name": "Docs products",
    "description": "Prices, per branch",
    "access": "rw",
    "partition_field": "branch",
    "retention_mode": "never",
    "retention_value": null,
    "storage_limit_bytes": null,
    "cursor": 0,
    "records": 0,
    "bytes": 0,
    "updated_at": null,
    "created_at": "2026-10-27T20:10:00.663Z"
  }
}

The same key again

curl -X POST "https://api.ardaform.net/v1/datasets" \
  -H "Authorization: Bearer $ARDA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Docs products","key":"zz-docs-products"}'
Answer · 409 Conflictran live 2026-10-27 · test project
{
  "error": {
    "code": "DATASET_EXISTS",
    "message": "A dataset with the key 'zz-docs-products' already exists in this project."
  }
}

GET/v1/datasets/{key}One dataset

One dataset. updated_at is when its data last changed — null until the first write — and cursor the sync number it is at.

Key scope datasets:r — read datasets

Path parameters

NameType
keystring
pattern ^[a-z0-9][a-z0-9_-]{0,79}$
The dataset's key — what you chose, not an id.

Answer 200 The dataset

FieldType
datasetDatasetA named collection of records — a catalogue, a price list, a transaction log. It belongs to the project the key was made on.

Errors

StatusWordWhen
403OUT_OF_SCOPEThe key's scopes do not allow this area and verb (or this dataset, for records). A key edited before an area existed has no entry for it and is refused there until it is edited again in the portal.
404NO_DATASETNo dataset with that key on the key's project.
404NO_SPACENo space with that id or slug in the key's project — in /v1/spaces/{space}, a space field or parameter, or the Arda-Space header. unassigned is no space (the devices in none). Another project's space answers the same, so nothing can be probed for.

And, like every keyed call: NO_KEY BAD_KEY KEY_REVOKED KEY_EXPIRED ACCOUNT_INACTIVE NO_API_ACCESS RATE_LIMITED INTERNAL.

curl "https://api.ardaform.net/v1/datasets/zz-docs-products" \
  -H "Authorization: Bearer $ARDA_KEY"
Answer · 200 OKran live 2026-10-27 · test project
{
  "dataset": {
    "key": "zz-docs-products",
    "name": "Docs products",
    "description": "Prices, per branch",
    "access": "rw",
    "partition_field": "branch",
    "retention_mode": "never",
    "retention_value": null,
    "storage_limit_bytes": null,
    "cursor": 8,
    "records": 3,
    "bytes": 186,
    "updated_at": "2026-10-27T20:10:03.502Z",
    "created_at": "2026-10-27T20:10:00.663Z"
  }
}

A key the project does not have

curl "https://api.ardaform.net/v1/datasets/zz-docs-nothing" \
  -H "Authorization: Bearer $ARDA_KEY"
Answer · 404 Not Foundran live 2026-10-27 · test project
{
  "error": {
    "code": "NO_DATASET",
    "message": "No dataset with the key 'zz-docs-nothing' in this project."
  }
}

PATCH/v1/datasets/{key}Change a dataset

Changes what is sent and nothing else. The key cannot change: your devices address the dataset by it.

  • retention_mode and retention_value are checked as a pair: send one and the other is read from the dataset. Switching to space or days needs its number.
  • ⚠️ Changing partition_field re-lifts every record's partitions in the same transaction and moves every record above every sync cursor — each syncing client receives the whole dataset again. The answer then carries repartitioned, next to dataset.

Key scope datasets:w — write datasets

Path parameters

NameType
keystring
pattern ^[a-z0-9][a-z0-9_-]{0,79}$
The dataset's key — what you chose, not an id.

Body

FieldType
namestring
at least 1 character
For people. Trimmed; longer than 100 characters is CUT to 100, not refused.
descriptionstringWhat it is for. Trimmed; empty clears it.
accessstring — rw or roWhat the app ON THE DEVICE may do. ro is read-only there; any other value is stored as rw. Does not restrict this API.
partition_fieldstringThe record field that splits the dataset, and the query parameter you filter on. Empty clears it. ⚠️ Changing it on PATCH re-lifts every record and moves every one above your sync cursor.
retention_modestring — never or days or spacenever (the default) keeps everything and refuses writes once full; days drops records older than retention_value days on each write; space drops the oldest once retention_value per cent of storage_limit_bytes is used.
retention_valueinteger
≥ 1
Days for days (≥ 1), per cent for space (1–100). Ignored and stored as null for never. On PATCH, the mode and the value are validated as a pair — the one not sent is read from the dataset.
storage_limit_bytesinteger
≥ 1
A ceiling on this dataset alone, inside the account allowance. Anything but a positive number is stored as null: no ceiling of its own.
keystringAccepted only if it is the key already — anything else is KEY_IMMUTABLE.

Answer 200 Saved

FieldType
datasetDatasetA named collection of records — a catalogue, a price list, a transaction log. It belongs to the project the key was made on.
repartitionedintegerOnly when partition_field changed: how many records were re-lifted. Every one of them also moved above your sync cursor — expect a full window from /changes after this.

Errors

StatusWordWhen
400KEY_IMMUTABLEA PATCH sent a different dataset key. A key never changes — your devices address the dataset by it.
400BAD_REQUESTThe body is missing a required field, has one of the wrong type or length, or (on PUT /v1/content/…) arrived shorter or longer than its Content-Length. The message says which.
400BAD_RETENTIONretention_mode is not never, days or space, or its retention_value is missing or out of range (days ≥ 1; space 1–100).
400NOTHING_TO_DOA PATCH sent no field it can change (a dataset with none of its fields; a device policy with no key).
403OUT_OF_SCOPEThe key's scopes do not allow this area and verb (or this dataset, for records). A key edited before an area existed has no entry for it and is refused there until it is edited again in the portal.
404NO_DATASETNo dataset with that key on the key's project.
404NO_SPACENo space with that id or slug in the key's project — in /v1/spaces/{space}, a space field or parameter, or the Arda-Space header. unassigned is no space (the devices in none). Another project's space answers the same, so nothing can be probed for.
409SPACE_DEFINEDInside a private space (Arda-Space), a dataset cannot be made, changed or dropped: its datasets are the project's, mirrored. Do it without the header and every private space follows; its records are its own.

And, like every keyed call: NO_KEY BAD_KEY KEY_REVOKED KEY_EXPIRED ACCOUNT_INACTIVE NO_API_ACCESS RATE_LIMITED INTERNAL BAD_JSON TOO_LARGE.

curl -X PATCH "https://api.ardaform.net/v1/datasets/zz-docs-products" \
  -H "Authorization: Bearer $ARDA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"description":"Prices, per branch — fed nightly","retention_mode":"days","retention_value":400}'
Answer · 200 OKran live 2026-10-27 · test project
{
  "dataset": {
    "key": "zz-docs-products",
    "name": "Docs products",
    "description": "Prices, per branch — fed nightly",
    "access": "rw",
    "partition_field": "branch",
    "retention_mode": "days",
    "retention_value": 400,
    "storage_limit_bytes": null,
    "cursor": 8,
    "records": 3,
    "bytes": 186,
    "updated_at": "2026-10-27T20:10:03.502Z",
    "created_at": "2026-10-27T20:10:00.663Z"
  }
}

Trying to rename it

curl -X PATCH "https://api.ardaform.net/v1/datasets/zz-docs-products" \
  -H "Authorization: Bearer $ARDA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"key":"products"}'
Answer · 400 Bad Requestran live 2026-10-27 · test project
{
  "error": {
    "code": "KEY_IMMUTABLE",
    "message": "A dataset key cannot be changed; your devices address the dataset by it."
  }
}

Switching to space while the value is still 400 days

curl -X PATCH "https://api.ardaform.net/v1/datasets/zz-docs-products" \
  -H "Authorization: Bearer $ARDA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"retention_mode":"space"}'
Answer · 400 Bad Requestran live 2026-10-27 · test project
{
  "error": {
    "code": "BAD_RETENTION",
    "message": "retention_value is a percentage for \"space\" mode, so it cannot be over 100"
  }
}

DELETE/v1/datasets/{key}Delete a dataset and every record in it

Deletes the dataset and every record in it, and says how many records went. There is no undo. A dataset your app's manifest declares is created again by the next pack — empty.

Key scope datasets:d — delete datasets

Path parameters

NameType
keystring
pattern ^[a-z0-9][a-z0-9_-]{0,79}$
The dataset's key — what you chose, not an id.

Answer 200 Deleted

FieldType
deletedobject
deleted.datasetstringThe key.
deleted.recordsintegerHow many records went with it.

Errors

StatusWordWhen
403OUT_OF_SCOPEThe key's scopes do not allow this area and verb (or this dataset, for records). A key edited before an area existed has no entry for it and is refused there until it is edited again in the portal.
404NO_DATASETNo dataset with that key on the key's project.
404NO_SPACENo space with that id or slug in the key's project — in /v1/spaces/{space}, a space field or parameter, or the Arda-Space header. unassigned is no space (the devices in none). Another project's space answers the same, so nothing can be probed for.
409SPACE_DEFINEDInside a private space (Arda-Space), a dataset cannot be made, changed or dropped: its datasets are the project's, mirrored. Do it without the header and every private space follows; its records are its own.

And, like every keyed call: NO_KEY BAD_KEY KEY_REVOKED KEY_EXPIRED ACCOUNT_INACTIVE NO_API_ACCESS RATE_LIMITED INTERNAL.

curl -X DELETE "https://api.ardaform.net/v1/datasets/zz-docs-products" \
  -H "Authorization: Bearer $ARDA_KEY"
Answer · 200 OKran live 2026-10-27 · test project
{
  "deleted": {
    "dataset": "zz-docs-products",
    "records": 0
  }
}

Retention

What a dataset does when it runs out of room is its own setting, because only you know which records matter.

retention_moderetention_valueWhat happens
never (default)ignored, stored as nullNothing is deleted. Once the dataset reaches its own storage_limit_bytes, writes are refused: 403 DATASET_FULL.
daysdays, at least 1On each write, records older than that are dropped.
space1–100, a percentageOn each write, the oldest records are dropped until the dataset is under that share of its storage_limit_bytes. Without a storage_limit_bytes it drops nothing.
  • Retention runs during a write, after it is stored, and that write's answer says how many records went in pruned. A dataset nobody writes to is not pruned.
  • Records a retention drop removes appear in a sync's deleted (keyed records only).
  • The project's and the account's ceilings still refuse a write that would pass them: see Storage.

The Dataset shape

Dataset

A named collection of records — a catalogue, a price list, a transaction log. It belongs to the project the key was made on.

FieldType
keystringHow you address it, and what your own code says. Lower-case letters, digits, hyphen and underscore, starting with a letter or digit. Immutable once created.
namestringFor people. Change it freely.
descriptionstring, nullableOptional. What it is for.
accessstring — rw or roWhat the app ON THE DEVICE may do. It does not restrict this API — a read-only dataset still has to be filled, and this is how.
partition_fieldstring, nullableThe field on a record that splits the dataset, and the query parameter you filter on. Several devices may share one value — three tills in one shop share their sales. The field may hold a LIST, which puts one record in several partitions at once: one price four branches share is one record, not four copies. Null means the dataset is not partitioned.
retention_modestring — never or days or spaceWhat happens when the dataset runs out of room. never keeps everything and REFUSES new records once it is full — nothing already written is thrown away. days drops records older than retention_value days. space drops the oldest once retention_value per cent of the limit is used, so the dataset rolls rather than stops.
retention_valueinteger, nullableDays for days, per cent for space. Null for never, and setting it there is ignored.
storage_limit_bytesinteger, nullableA ceiling on this dataset alone, inside the account allowance. Null means it is bounded only by the account.
cursorintegerThis dataset's current sync number. A client that has just read the whole dataset takes this as the since for its first incremental call. Opaque — compare it, never do arithmetic on it.
recordsintegerCounted at read time, never stored — a kept counter is a second copy of the truth and the first half-write puts the two out of agreement for ever.
bytesintegerThe payloads as stored, and what the storage allowance is measured against. No index or row overhead — it is a number you could work out yourself from the data you sent.
updated_atstring (date-time), nullableWhen the DATA last changed, not the row — a rename does not move it. Null until the first write. UTC, like every timestamp here.
created_atstring (date-time)When the dataset was made. UTC, like every timestamp here.