Create a space
space_id is not a settable field. Sending one is rejected with 422. So is a
space_type that is not on the list above — a request asking for a type Anona does not
have is refused rather than quietly creating a plain space.
Response 201 Created
space_type in the response is what was applied, not what you asked for. It is
null when nothing was applied: when you sent nothing, when you sent blank, and also
when a type was requested and could not be applied. The space is created and usable
either way — applying a type never fails the create — so this field is how you tell a
seeded space from a plain one without a second call. If it comes back null after you
asked for a type, either set the pieces yourself or delete the space and create it
again.
space_id is the name you chose. There is no separate generated id. The
name you pass on create is the identifier every other endpoint takes, so pick it
deliberately. Every memory operation requires it.
Space types
A new space is an empty box. A space type fills some of it in at creation: what the space is for, what its extraction pass should keep, how it weighs what it is told, and two standing questions worth keeping an answer to. Pass one asspace_type on create. That is the only moment a type can be applied —
there is no endpoint that converts an existing space, because everything a type sets is
already settable on its own.
A dial a type does not move stays at its default of 3. The dials are moved only where
the use case really differs, and they change how a space weighs what it is told when it
synthesizes — never what a plain retrieve returns. See
space profile.
What a type sets, and how to change it
Three things, all of which stay editable afterwards.
A space type is a starting point, not a mode. Nothing is locked, nothing behaves differently
later because of the type you chose, and a space you seeded is the same kind of space as
one you configured by hand. It writes configuration only: no memories are created, and
your own writes are unaffected.
space_type is also not stored as a property of the space, and it is not a field of
the read responses at all: GET /v1/spaces/{space_id} and GET /v1/spaces omit it
rather than reporting it null, because there would be nothing to report — what a
type did is the mission, the guidance and the models it wrote, and each of those reads
back on its own endpoint.
The starting models are empty on purpose
A seeded space’s two models have no content until the space has memories worth
reading. This is expected, not a failed apply.
mode: "delta" and refresh_on_new_memories: true, so it
writes itself the first time newly recorded memories are consolidated, and rewrites
itself as the space learns more. Until then GET /v1/spaces/{space_id}/models lists
them with content: null.
Generating an answer at creation would have been worse than empty. The space is seconds
old and holds nothing, so the pass would have spent your credits to store “I have no
information about this” as the model’s content — and that sentence then reads as the
answer, to you and to any agent reading the model, until something replaces it. An empty
model is honest about what the space knows; a confident empty one is not.
Record something and let it consolidate, then read the models back. To fill one sooner,
POST /v1/spaces/{space_id}/models/{model_id}/refresh writes it now; that call is
billed like reason.
What each type starts with
Model ids are stable, so a seeded model can be refreshed, edited or deleted by id.List the space types
200 OK
blank last. mission
is the exact text the type writes to the space profile — worth reading before you choose,
and worth editing afterwards if it is nearly right. disposition carries only the dials
that type moves, and models only their names and questions; the answers are written in
the space itself.
List spaces
200 OK
Get space details
200 OK
Delete a space
204 No Content.
Space members
Spaces can optionally have named members within your organization.The membership endpoints are control-plane calls: they need a dashboard session
with the owner or admin role, not an API key. An API key gets
401 session_required;
a developer or viewer session gets 403 insufficient_role.
The org-wide state exists for spaces that pre-date membership: creating a space
through the API or dashboard automatically writes a member row for the creator, so
a new space is member-only from the moment it exists.
Invited over Slack
A space bound to a Slack channel also has people who were offered it over Slack and have not taken it. They have no Anona account yet, so they are listed separately rather than as half-built members:200 OK
state is pending (asked, no answer yet), declined (said no) or expired
(fourteen days passed). Anyone who accepted is absent, because they hold a real
membership row and the member listing above already names them.
There is no email field and there will not be one: the Slack roster is read
through users:read, never users:read.email. display_name is null for a
roster synced before names were stored.
Rules
A rule is a standing instruction the space must follow whenever it answers a question. It is not a preference and it is not a tone setting — those are the disposition dials on the space profile, which only shift how an answer is worded. A rule applies to every answer the space gives, and it overrides what the memories say:A rule
Rules are owner-only and free. They consume no credits, deliberately — an
organization out of credits must still be able to switch off a rule that is
producing bad answers.
List the rules on a space
200 OK
Add a rule
Response
201 Created, carrying the stored rule in the shape above.
How many rules a space can have
A space may have 25 active rules at once. A create or an activation that would make it 26 is refused with422 too_many_active_rules.
The cap counts active rules only, so switching one off always frees its slot
without deleting anything:
Edit or remove a rule
PATCH is a partial edit — only the fields you send change, and the rest are
left exactly as they are. That is deliberate: switching a rule off or lifting its
priority should never mean resending the text of something enforced on every
answer, where a typo is expensive.
A PATCH that names no field is refused with 422 empty_update rather than
treated as a no-op.
204 No Content. Answers already given are unchanged; later ones
stop obeying it.
Which rules shaped an answer
POST /v1/reason returns
rules_applied, naming the rules that actually applied to that answer:
GET /v1/spaces/{space_id}/rules — while this rides every answer, and a space
may hold 25 active rules of 2,000 characters each.
An empty list is a real finding, not a missing field: it says no rule applied.
That is what separates the memories are wrong from a rule said otherwise,
which need opposite fixes.
Scoping a rule
tags narrow when a rule applies. Most rules want none.
- A rule with no tags applies to every question the space is asked. This is the usual case, and the one to reach for.
- A tagged rule applies only to a question filtered to matching tags with
tag_groups. A question that names no filter is answered under the untagged rules alone, so a tagged rule can never reach a question that did not ask for it. - A question scoped to one end user (
user_id,agent_id,session_id) is likewise answered under the untagged rules alone.
anona: is refused with 422 reserved_tag. That namespace
carries scope, and a rule able to claim it could pin itself to one end user’s
questions.
From the SDKs
async_ twins in Python and are promise-based in TypeScript.
update_rule / updateRule send only the fields you pass.
In the SDKs
async_ variants). Membership management is
dashboard/session-only, so it is deliberately in neither API-key SDK.
Error responses
There is no
429 here. The space endpoints are control-plane calls: they spend no
credits and are not rate-limited, so they carry none of the X-RateLimit-* headers
either. See Usage.
See the full error reference.
Next steps
Memories API
Store and manage memories.
Errors
The complete error code reference.