Open a workspace
const url = 'https://api.facteur.eu/v1/workspaces';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"name":"example","tag":"example","color":"example"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://api.facteur.eu/v1/workspaces \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "name": "example", "tag": "example", "color": "example" }'The slug is derived from the name and made unique within the organization; it is what every other route addresses the workspace by.
A workspace created by a key does not enter that key’s scope, and no member row is written for it — a key is not a person, and attributing the act to the human who minted the key is the one thing a trace must not do.
Authorizations
Section titled “Authorizations”Request Bodyrequired
Section titled “Request Bodyrequired”object
One of eight names, so no free value reaches the stylesheet.
Examplegenerated
{ "name": "example", "tag": "example", "color": "example"}Responses
Section titled “Responses”The workspace.
object
How every other route addresses this workspace. The internal id never leaves.
The workspace every member of the organization reaches without being added to it.
Null on the default workspace, which has no member rows because everyone is in it.
The ceilings in force. monthly.messages always carries the figure being
enforced, whether the customer wrote a number or a share of the plan, so
an interface renders it without knowing which. percent is present only
when a share is how it was written.
object
Messages accepted per day, or null for no daily ceiling.
object
The ceiling being enforced. Null next to a percent means the
plan sells no monthly envelope for that share to apply to, so the
rule holds nothing back — shown rather than hidden.
Present only when the ceiling was written as a share of the plan.
What happens to mail past the ceiling.
Examplegenerated
{ "slug": "example", "name": "example", "createdAt": "2026-04-15T12:00:00Z", "isDefault": true, "tag": "example", "color": "example", "inboxCount": 1, "memberCount": 1, "messagesThisMonth": 1, "canManage": true, "quota": { "daily": 1, "monthly": { "messages": 1, "percent": 1 }, "onExceed": "example" }}NAME_REQUIRED, NAME_TOO_LONG, NAME_NOT_SLUGGABLE (nothing in the name survives as a URL), TAG_INVALID, COLOR_INVALID, or INVALID_JSON.
Every failure has this shape. Branch on error.code; error.message is
written for a human and is in French today.
object
object
The stable, machine-readable reason.
A sentence for whoever is reading the failure. Not part of the contract.
Example
{ "error": { "code": "SCOPE_INSUFFICIENT" }}No usable credential. UNAUTHENTICATED when none was presented,
KEY_UNKNOWN, KEY_REVOKED or KEY_EXPIRED when a key was — kept
distinct on purpose, because “mint a new one” and “somebody took this
away from you” are different facts to whoever is reading a red pipeline.
Every failure has this shape. Branch on error.code; error.message is
written for a human and is in French today.
object
object
The stable, machine-readable reason.
A sentence for whoever is reading the failure. Not part of the contract.
Example
{ "error": { "code": "SCOPE_INSUFFICIENT" }}WORKSPACE_LIMIT_REACHED — the plan includes fewer workspaces than this would make.
A refusal that a different plan would not produce. Carries what to change.
object
object
Examplegenerated
{ "error": { "code": "example", "message": "example", "currentPlan": "example", "requiredPlan": "example" }}SCOPE_INSUFFICIENT (the message names the scope this request wanted and
the ones the key holds), KEY_CANNOT_REACH (no scope opens this route to
a key at all), FORBIDDEN, or one of the stated refusals listed under
x-api-key-refusals.
Every failure has this shape. Branch on error.code; error.message is
written for a human and is in French today.
object
object
The stable, machine-readable reason.
A sentence for whoever is reading the failure. Not part of the contract.
Example
{ "error": { "code": "SCOPE_INSUFFICIENT" }}