Spaces.

The groups of your project's devices. Each space runs a branch — a Developer space on dev beside the Default on main — and a private one keeps records, files and settings of its own: a customer of yours, kept apart from the rest. Make and change them here, move devices between them, and act inside one with the Arda-Space header.

Working — v1 at api.ardaform.net

About spaces

Every device of a project is in one space, or in none. A space decides two things about the devices in it: which branch they run, and — when it is private — whose data they read.

  • A branch each. A space names one of the project's branches; its devices run that branch's app, and changing the branch moves every device in the space onto it. A Developer space on dev beside the Default on main is the whole of a test bench.
  • Private: data of its own. A private space's records, files and setting values are its own — kept apart from the project's and from every other space's, as two projects are. Its datasets and settings are the app's, made in it when it turns private and kept in step after every push; each setting starts from the project's value. This is the space for one of your customers: a till company's shop, with its own products and its own sales. The portal shows a private space's data only as a summary; your system reads and writes it here, with Arda-Space.
  • Shared: the project's data. A space that is not private reads the project's own records and files, like the Default.
  • Its own limits. max_devices caps how many devices it may hold; a private one may have storage_mb for its own data. Both sit inside the account's own limits; leave them out for none.
  • The plan decides how many. limits.max_spaces in GET /v1/auth is how many the account may make beyond each project's Default, across all its projects. A space is not a project to the plan's project limit, and a private one's datasets do not count against the dataset limit.
  • Managed with the project's key, area spaces, all four verbs. A key made before 2026-10-27 has no spaces entry in its scopes until it is edited in the portal.
  • Licences. On an app bought from the marketplace, the devices of every space share the project's licences — first activated, first licensed.

Default and Unassigned

Two names are reserved, and you can use them wherever this API takes a space:

  • default — every project's Default space. A device lands in it unless it is told another. It keeps its name and slug, it is never private (it is the project's data), and it is never removed (409 SPACE_DEFAULT). Its branch and device limit change like any space's.
  • unassigned — no space at all: a device activated without one, or left by a space that was removed. It runs no app — its screen says it is waiting for a space — and nothing is deployed to it until it is moved into one. Nothing on it is deleted, and a new player version reaches it as soon as one is out. Register a stock of devices into unassigned, and move each into its customer's space when it ships.

Acting inside a space

Send Arda-Space: <id or slug> with any other call and it acts inside that space. For a private space the call reads and writes the space's own data — its records, files and settings. For any other the data is the project's, so the header changes nothing there. On /v1/devices it narrows the list to that space and activates into it. The key's scopes still decide what the call may do; a space that is not one of this project's is 404 NO_SPACE.

  • Inside a private space, its datasets are the app's. A dataset cannot be made, changed or dropped there (409 SPACE_DEFINED): change it on the project, without the header, and every private space follows.
  • /v1/spaces itself refuses the header: spaces are managed from their project.

Which space a call acted in — GET /v1/auth says

curl "https://api.ardaform.net/v1/auth" \
  -H "Authorization: Bearer $ARDA_KEY" \
  -H "Arda-Space: zz-docs-cake-co"
Answer · 200 OKran live 2026-10-27 · test project · a list shortened
{
  "key": {
    "name": "api npm test",
    "prefix": "af_••••••••"
  },
  "project": {
    "slug": "api-test-zz-docs-cake-co",
    "name": "Cake & Co, Soho Road",
    "status": "active"
  },
  "space": {
    "id": 850,
    "slug": "zz-docs-cake-co",
    "name": "Cake & Co, Soho Road",
    "private": true
  }
}

The customer's own shops, written into its copy of the app's dataset

curl -X POST "https://api.ardaform.net/v1/datasets/zz-docs-branches/records" \
  -H "Authorization: Bearer $ARDA_KEY" \
  -H "Arda-Space: zz-docs-cake-co" \
  -H "Content-Type: application/json" \
  -d '[{"id":"1","name":"Soho Road"},{"id":"2","name":"Harborne"}]'
Answer · 200 OKran live 2026-10-27 · test project
{
  "dataset": "zz-docs-branches",
  "written": 2,
  "appended": 0,
  "refused": 0,
  "exceptions": [],
  "cursor": 2,
  "pruned": 0
}

A dataset made inside a private space is refused

curl -X POST "https://api.ardaform.net/v1/datasets" \
  -H "Authorization: Bearer $ARDA_KEY" \
  -H "Arda-Space: zz-docs-cake-co" \
  -H "Content-Type: application/json" \
  -d '{"name":"Own list","key":"zz-docs-own"}'
Answer · 409 Conflictran live 2026-10-27 · test project
{
  "error": {
    "code": "SPACE_DEFINED",
    "message": "A space's datasets are its parent project's — mirrored from them. Make, change or drop the dataset on the parent (without `Arda-Space`) and every space follows; its records are the space's own."
  }
}

GET/v1/spacesThe spaces of the key's project

Every space of the key's project — the Default first, then by name — with the branch it runs and how many devices it holds. unassigned counts the devices in none. Page through a long list with limit and offset; total counts them all.

Key scope spaces:r — read spaces

Query parameters

NameType
limitinteger
1–1000 · default 100
Page size, 1–1000, default 100. Out of range is clamped to the range, not refused; a value that is not a number is the default.
offsetinteger
≥ 0 · default 0
How many to skip. Negative or not a number is 0.

Answer 200 The spaces

FieldType
spacesarray of object
totalintegerEvery space of the project, whatever the page.
unassignedintegerThe devices in no space — nothing is deployed to them.

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.
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.
409NOT_A_PARENTThe key's project is a private space's store, which has no spaces of its own.

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/spaces" \
  -H "Authorization: Bearer $ARDA_KEY"
Answer · 200 OKran live 2026-10-27 · test project
{
  "spaces": [
    {
      "id": 1,
      "slug": "default",
      "name": "Default",
      "default": true,
      "branch": "main",
      "private": false,
      "devices": 1,
      "max_devices": null,
      "storage_mb": null,
      "created_at": "2026-10-27T19:53:15.181Z"
    },
    {
      "id": 850,
      "slug": "zz-docs-cake-co",
      "name": "Cake & Co, Soho Road",
      "default": false,
      "branch": "main",
      "private": true,
      "devices": 0,
      "max_devices": 3,
      "storage_mb": 50,
      "created_at": "2026-10-27T20:10:20.494Z"
    }
  ],
  "total": 2,
  "unassigned": 0
}

POST/v1/spacesMake a space

A new space is empty: activate devices into it, or move them in.

  • name is required. slug is made from it when you leave it out; branch is the project's default branch unless you name one.
  • private: true gives it data of its own, made at once: the app's datasets and settings are in it before the answer comes back. mirrored: false means the device service did not answer in time; the definitions arrive when its first device connects.
  • Refused when the plan has no spaces (403 NO_SPACES), when they are all made (403 LIMIT_REACHED, with limit and used), for a slug the project has or a reserved one (409 SLUG_TAKEN), and for a branch the project does not have (400 NO_BRANCH).

Key scope spaces:a — add spaces

Body

FieldType
name requiredstring
at most 100 characters
What the portal calls it. Required.
slugstring
at most 40 characters
How this API names it: small letters, digits and dashes. From the name when left out.
branchstring
at most 80 characters
The branch its devices run. The project's default branch when left out.
privatebooleanData of its own. False when left out: the project's data.
max_devicesinteger, nullable
≥ 1
The most devices it may hold.
storage_mbinteger, nullable
≥ 1
A private space's own storage limit, in MB.

Answer 201 Made

FieldType
spaceobjectOne space of the key's project: a group of its devices, the branch they run, and — when private — data of their own.
space.idintegerThe space's id. Address it by this or by slug.
space.slugstringHow this API names it: small letters, digits and dashes, unique in the project. default is the Default space's; unassigned is reserved for the devices in none.
space.namestringWhat the portal calls it.
space.defaultbooleanThe project's Default space: every project has one, a device lands in it unless told another, and it keeps its name and slug and is never removed.
space.branchstringThe branch its devices run. Changing it moves every device in the space onto that branch.
space.privatebooleanIts own records, files and setting values, kept apart from the project's — reached with Arda-Space. False: the project's own data.
space.devicesintegerThe devices in it.
space.max_devicesinteger, nullableThe most devices it may hold, or null for only the account's limit.
space.storage_mbinteger, nullableA private space's own storage limit, in MB; null when it has none or is not private.
space.created_atstring (date-time)When it was made. UTC.
space.used_bytesinteger, nullableOnly on GET /v1/spaces/{space}: what a private space's own records and files take now; null on any other.
mirroredboolean, nullableA private space: the app's datasets and settings are already in it. Null on any other.

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.
400NO_BRANCHThe project has no branch of that name — a space runs one of the project's branches.
403NO_SPACESThe account's plan has no spaces beyond each project's Default (max_spaces 0).
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.
409NOT_A_PARENTThe key's project is a private space's store, which has no spaces of its own.
409SLUG_TAKENAnother space of this project has that slug — or it is one of the two reserved: default (the Default space) and unassigned (a device in none).

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/spaces" \
  -H "Authorization: Bearer $ARDA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Cake & Co, Soho Road","slug":"zz-docs-cake-co","private":true,"max_devices":3,"storage_mb":50}'
Answer · 201 Createdran live 2026-10-27 · test project
{
  "space": {
    "id": 850,
    "slug": "zz-docs-cake-co",
    "name": "Cake & Co, Soho Road",
    "default": false,
    "branch": "main",
    "private": true,
    "devices": 0,
    "max_devices": 3,
    "storage_mb": 50,
    "created_at": "2026-10-27T20:10:20.494Z"
  },
  "mirrored": true
}

A slug another space has

curl -X POST "https://api.ardaform.net/v1/spaces" \
  -H "Authorization: Bearer $ARDA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Cake & Co again","slug":"zz-docs-cake-co"}'
Answer · 409 Conflictran live 2026-10-27 · test project
{
  "error": {
    "code": "SLUG_TAKEN",
    "message": "A space of this project already has the slug 'zz-docs-cake-co'.",
    "slug": "zz-docs-cake-co"
  }
}

GET/v1/spaces/{space}One space

One space, by its id or slug — default is the Default — with used_bytes: what a private space's own records and files take now. Null on any other.

Key scope spaces:r — read spaces

Path parameters

NameType
spacestring
at most 100 characters
The space's id or its slug — default is the Default space. Another project's space is 404 NO_SPACE, the same as none.

Answer 200 The space

FieldType
spaceobjectOne space of the key's project: a group of its devices, the branch they run, and — when private — data of their own.
space.idintegerThe space's id. Address it by this or by slug.
space.slugstringHow this API names it: small letters, digits and dashes, unique in the project. default is the Default space's; unassigned is reserved for the devices in none.
space.namestringWhat the portal calls it.
space.defaultbooleanThe project's Default space: every project has one, a device lands in it unless told another, and it keeps its name and slug and is never removed.
space.branchstringThe branch its devices run. Changing it moves every device in the space onto that branch.
space.privatebooleanIts own records, files and setting values, kept apart from the project's — reached with Arda-Space. False: the project's own data.
space.devicesintegerThe devices in it.
space.max_devicesinteger, nullableThe most devices it may hold, or null for only the account's limit.
space.storage_mbinteger, nullableA private space's own storage limit, in MB; null when it has none or is not private.
space.created_atstring (date-time)When it was made. UTC.
space.used_bytesinteger, nullableOnly on GET /v1/spaces/{space}: what a private space's own records and files take now; null on any other.

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.
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.
409NOT_A_PARENTThe key's project is a private space's store, which has no spaces of its own.

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/spaces/zz-docs-cake-co" \
  -H "Authorization: Bearer $ARDA_KEY"
Answer · 200 OKran live 2026-10-27 · test project
{
  "space": {
    "id": 850,
    "slug": "zz-docs-cake-co",
    "name": "Cake & Co, Soho Road",
    "default": false,
    "branch": "main",
    "private": true,
    "devices": 0,
    "max_devices": 3,
    "storage_mb": 50,
    "created_at": "2026-10-27T20:10:20.494Z",
    "used_bytes": 0
  }
}

An id or slug that is not this project's

curl "https://api.ardaform.net/v1/spaces/cust-9999" \
  -H "Authorization: Bearer $ARDA_KEY"
Answer · 404 Not Foundran live 2026-10-27 · test project
{
  "error": {
    "code": "NO_SPACE",
    "message": "No space 'cust-9999' in this key's project.",
    "space": "cust-9999"
  }
}

PATCH/v1/spaces/{space}Change a space

Send any of name, slug, branch, private, max_devices, storage_mb; null clears a limit.

  • A new branch moves every device in the space onto it — each takes the new app as its branch's apply policy says.
  • private: true gives the space data of its own; its devices drop the project's data and walk the space's. private: false deletes that data and its devices go back to the project's — it needs ?confirm=1.
  • A lower max_devices stops new devices joining and keeps the ones it holds.

Key scope spaces:w — write spaces

Path parameters

NameType
spacestring
at most 100 characters
The space's id or its slug — default is the Default space. Another project's space is 404 NO_SPACE, the same as none.

Query parameters

NameType
confirmstring — 1Must be 1 to turn private off: the space's own data is deleted.

Body

FieldType
namestring
at most 100 characters
What the portal calls it.
slugstring
at most 40 characters
How this API names it.
branchstring
at most 80 characters
The branch its devices run.
privatebooleanData of its own. Turning it off deletes that data (?confirm=1).
max_devicesinteger, nullable
≥ 1
The most devices it may hold, or null for none of its own.
storage_mbinteger, nullable
≥ 1
A private space's storage limit in MB, or null.

Answer 200 Changed

FieldType
spaceobjectOne space of the key's project: a group of its devices, the branch they run, and — when private — data of their own.
space.idintegerThe space's id. Address it by this or by slug.
space.slugstringHow this API names it: small letters, digits and dashes, unique in the project. default is the Default space's; unassigned is reserved for the devices in none.
space.namestringWhat the portal calls it.
space.defaultbooleanThe project's Default space: every project has one, a device lands in it unless told another, and it keeps its name and slug and is never removed.
space.branchstringThe branch its devices run. Changing it moves every device in the space onto that branch.
space.privatebooleanIts own records, files and setting values, kept apart from the project's — reached with Arda-Space. False: the project's own data.
space.devicesintegerThe devices in it.
space.max_devicesinteger, nullableThe most devices it may hold, or null for only the account's limit.
space.storage_mbinteger, nullableA private space's own storage limit, in MB; null when it has none or is not private.
space.created_atstring (date-time)When it was made. UTC.
space.used_bytesinteger, nullableOnly on GET /v1/spaces/{space}: what a private space's own records and files take now; null on any other.
movedintegerThe devices moved onto its new branch or data.

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.
400NO_BRANCHThe project has no branch of that name — a space runs one of the project's branches.
400CONFIRM_REQUIREDEmptying a whole dataset, removing a space (its devices to Unassigned, a private one's own data deleted), or turning a space's private off (its own data deleted) needs ?confirm=1.
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_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.
409NOT_A_PARENTThe key's project is a private space's store, which has no spaces of its own.
409SLUG_TAKENAnother space of this project has that slug — or it is one of the two reserved: default (the Default space) and unassigned (a device in none).
409SPACE_DEFAULTThe Default space keeps its name and slug, uses the project's own data, and is never removed — every project has it. Its branch and max_devices change like any space's.

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/spaces/zz-docs-cake-co" \
  -H "Authorization: Bearer $ARDA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"max_devices":5,"storage_mb":null}'
Answer · 200 OKran live 2026-10-27 · test project
{
  "space": {
    "id": 850,
    "slug": "zz-docs-cake-co",
    "name": "Cake & Co, Soho Road",
    "default": false,
    "branch": "main",
    "private": true,
    "devices": 0,
    "max_devices": 5,
    "storage_mb": null,
    "created_at": "2026-10-27T20:10:20.494Z"
  },
  "moved": 0
}

The Default keeps its name

curl -X PATCH "https://api.ardaform.net/v1/spaces/default" \
  -H "Authorization: Bearer $ARDA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Main"}'
Answer · 409 Conflictran live 2026-10-27 · test project
{
  "error": {
    "code": "SPACE_DEFAULT",
    "message": "The Default space keeps its name and slug, and uses the project's own data."
  }
}

DELETE/v1/spaces/{space}Remove a space, its devices to Unassigned

Removes the space. Its devices go to Unassigned — still activated, running no app until they are moved — and a private space's own data is deleted. An install code made for it registers into Unassigned from then on. The Default is never removed. It cannot be undone, so it needs ?confirm=1; moved says how many devices went to Unassigned.

Key scope spaces:d — delete spaces

Path parameters

NameType
spacestring
at most 100 characters
The space's id or its slug — default is the Default space. Another project's space is 404 NO_SPACE, the same as none.

Query parameters

NameType
confirm requiredstring — 1Must be 1: removing a space cannot be undone.

Answer 200 Removed

FieldType
deletedbooleanAlways true.
spaceobjectOne space of the key's project: a group of its devices, the branch they run, and — when private — data of their own.
space.idintegerThe space's id. Address it by this or by slug.
space.slugstringHow this API names it: small letters, digits and dashes, unique in the project. default is the Default space's; unassigned is reserved for the devices in none.
space.namestringWhat the portal calls it.
space.defaultbooleanThe project's Default space: every project has one, a device lands in it unless told another, and it keeps its name and slug and is never removed.
space.branchstringThe branch its devices run. Changing it moves every device in the space onto that branch.
space.privatebooleanIts own records, files and setting values, kept apart from the project's — reached with Arda-Space. False: the project's own data.
space.devicesintegerThe devices in it.
space.max_devicesinteger, nullableThe most devices it may hold, or null for only the account's limit.
space.storage_mbinteger, nullableA private space's own storage limit, in MB; null when it has none or is not private.
space.created_atstring (date-time)When it was made. UTC.
space.used_bytesinteger, nullableOnly on GET /v1/spaces/{space}: what a private space's own records and files take now; null on any other.
movedintegerThe devices moved to Unassigned.

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.
400CONFIRM_REQUIREDEmptying a whole dataset, removing a space (its devices to Unassigned, a private one's own data deleted), or turning a space's private off (its own data deleted) needs ?confirm=1.
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.
409NOT_A_PARENTThe key's project is a private space's store, which has no spaces of its own.
409SPACE_DEFAULTThe Default space keeps its name and slug, uses the project's own data, and is never removed — every project has it. Its branch and max_devices change like any space's.

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/spaces/zz-docs-cake-co?confirm=1" \
  -H "Authorization: Bearer $ARDA_KEY"
Answer · 200 OKran live 2026-10-27 · test project
{
  "deleted": true,
  "space": {
    "id": 850,
    "slug": "zz-docs-cake-co",
    "name": "Cake & Co, Soho Road",
    "default": false,
    "branch": "main",
    "private": true,
    "devices": 0,
    "max_devices": 5,
    "storage_mb": null,
    "created_at": "2026-10-27T20:10:20.494Z"
  },
  "moved": 0
}

Without ?confirm=1

curl -X DELETE "https://api.ardaform.net/v1/spaces/zz-docs-cake-co" \
  -H "Authorization: Bearer $ARDA_KEY"
Answer · 400 Bad Requestran live 2026-10-27 · test project
{
  "error": {
    "code": "CONFIRM_REQUIRED",
    "message": "Removing space 'zz-docs-cake-co' moves its devices to Unassigned and deletes its own data; add ?confirm=1 to do it."
  }
}

Devices per space

Every device on /v1/devices says its space — null when it is Unassigned. GET /v1/devices?space=<id or slug> lists one space's devices, ?space=default the Default's and ?space=unassigned the ones in none; an Arda-Space header narrows the list the same way. PATCH /v1/devices/{uid} with {"space": …} moves one.

The devices in no space

curl "https://api.ardaform.net/v1/devices?space=unassigned" \
  -H "Authorization: Bearer $ARDA_KEY"
Answer · 200 OKran live 2026-10-27 · test project
{
  "devices": []
}

A device moved into a customer's private space

curl -X PATCH "https://api.ardaform.net/v1/devices/aa11aa11aa11aa11aa11a9e5" \
  -H "Authorization: Bearer $ARDA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"space":"zz-docs-cake-co"}'
Answer · 200 OKran live 2026-10-27 · test project
{
  "device": {
    "uid": "aa11aa11aa11aa11aa11a9e5",
    "name": "zz-docs-tablet",
    "os": "android",
    "arch": "arm64",
    "model": "Samsung SM-X200",
    "player": "v1.2.4",
    "branch": "main",
    "partition": null,
    "location": "Counter 1",
    "enrolled": false,
    "space": {
      "id": 850,
      "slug": "zz-docs-cake-co",
      "name": "Cake & Co, Soho Road"
    },
    "presence": "never",
    "online": false,
    "last_seen_at": null,
    "policy": {
      "exitAllowed": false,
      "kiosk": true,
      "orientation": "landscape"
    }
  },
  "moved": true,
  "applies": "when the device next connects"
}

POST/v1/devices/activateActivate a device by the code on its screen

What typing the code into the portal's Add device does, from your own system: the device showing code on its screen joins the key's project and starts the app its space runs. space names the space — an id or slug, default, or unassigned to register it into none; left out, the space Arda-Space names, else the Default. name, partition and location are what the portal would ask for. The area is devices, verb w.

  • A fresh device shows its code on its screen until it is activated; the example's code is shown by no device, so the answer is the refusal.
  • Refused when the account's devices are all used (403 LIMIT_REACHED), for a space that is not there (404 NO_SPACE), when no device is showing the code (404 NO_PENDING_DEVICE), when the space already holds its max_devices (409 SPACE_FULL), and for anything else the device service says (409 NOT_ACTIVATED, its words in reason).

Key scope devices:w — write devices

Body

FieldType
code requiredstringThe activation code the device shows. Required.
namestring
at most 100 characters
What to call it.
spacestring, nullableThe space it joins: id, slug, default, or unassigned / null for none.
partitionstring, nullable
at most 80 characters
Its data partition.
locationstring, nullable
at most 120 characters
Where it is.

Answer 201 Activated

FieldType
deviceobjectOne device of the key's project, as ArdaForm knows it — its identity, whether it is connected, and the policy ArdaForm holds for it. On one device, android is what the device last reported about itself.
device.uidstringThe device's id — 24 hex characters, the one on its page in the portal. How you address it.
device.namestringWhat it was named in the portal.
device.osstring, nullableThe player's operating system as it reports it: linux, android, …
device.archstring, nullableThe processor family: arm64, amd64, …
device.modelstring, nullableThe hardware, as the device reports it.
device.playerstring, nullableThe ArdaPlayer version it last reported.
device.branchstring, nullableThe code branch it runs — its space's. Null when it is Unassigned: it runs no app.
device.partitionstring, nullableIts data partition, or null when it has none.
device.locationstring, nullableWhere it is, as typed in the portal. Null when nothing was typed.
device.enrolledbooleanFalse once it was removed (wiped), or never activated — its record stays.
device.spaceobject, nullableThe space it is in: id, slug, name. Null when it is in none — Unassigned.
device.presencestring — online or offline or never, nullableThe one word every portal screen uses: online, offline, or never (never connected).
device.onlinebooleanConnected to ArdaForm right now.
device.last_seen_atstring (date-time), nullableWhen it was last heard from. Null when never. UTC.
device.policyobjectThe switches — and the screen's orientation — ArdaForm holds for this device and re-states to it on every check-in — only the keys that were ever set, {} when none. A key that is absent means the device keeps what it holds.
device.androidobject, nullableOnly on GET /v1/devices/{uid}, and only on an Android device: what the device last REPORTED about itself (its sysinfo.android) — kiosk is the screen's live lock state, exitAllowed what it applies now, deviceOwner whether kiosk can work at all, with release, model, appVersion and battery. It differs from policy until the device has checked in. Null on a Linux device; absent in the list.

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.
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.
404NO_PENDING_DEVICENo device is showing that activation code right now: check the code on its screen, and that the device is switched on and online.
409SPACE_FULLThe space already holds its max_devices.
409NOT_ACTIVATEDThe device service refused the activation — a code tried too often, a device already registered. reason says which.
502CONNECT_UNAVAILABLEThe device service did not answer — a webhook Test, an activation, or the wipes a space removal needs. Nothing was done; try again in a moment.

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.

A code no device is showing

curl -X POST "https://api.ardaform.net/v1/devices/activate" \
  -H "Authorization: Bearer $ARDA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"code":"KX7P-2M4Q","name":"Till 1","location":"Front counter","space":"zz-docs-cake-co"}'
Answer · 404 Not Foundran live 2026-10-27 · test project
{
  "error": {
    "code": "NO_PENDING_DEVICE",
    "message": "No device is showing that code right now — check the code on its screen, and that the device is online."
  }
}

The Space shape

Space

One space of the key's project: a group of its devices, the branch they run, and — when private — data of their own.

FieldType
idintegerThe space's id. Address it by this or by slug.
slugstringHow this API names it: small letters, digits and dashes, unique in the project. default is the Default space's; unassigned is reserved for the devices in none.
namestringWhat the portal calls it.
defaultbooleanThe project's Default space: every project has one, a device lands in it unless told another, and it keeps its name and slug and is never removed.
branchstringThe branch its devices run. Changing it moves every device in the space onto that branch.
privatebooleanIts own records, files and setting values, kept apart from the project's — reached with Arda-Space. False: the project's own data.
devicesintegerThe devices in it.
max_devicesinteger, nullableThe most devices it may hold, or null for only the account's limit.
storage_mbinteger, nullableA private space's own storage limit, in MB; null when it has none or is not private.
created_atstring (date-time)When it was made. UTC.
used_bytesinteger, nullableOnly on GET /v1/spaces/{space}: what a private space's own records and files take now; null on any other.