Organizations Namespace
- Category
- core
- Namespace
- organizations
Discover the organizations you belong to, read why a call was allowed or denied, create organizations, administer membership, inspect citation health, and drive the reversible organization-deletion lifecycle.
| Access | Method | Side Effect | Description |
|---|---|---|---|
| read | listMine | No | Returns an envelope, |
| read | getMyAccess | No | The answer to "why was I denied". Returns your live |
| write | createOrganization | Yes | The method is called |
| write | renameOrganization | Yes | Changes the display name and nothing else. The name is rejected rather than silently trimmed, so send the exact string you want stored. Requires an organization administrator role and the |
| write | setOrganizationVisibility | Yes | Sets one organization private or public. This is the only method whose |
| read | listMembers | No | Lists active members and pending invitations together in one envelope, |
| write | inviteMember | Yes | Creates the invitation and, by default, sends the email. The role is |
| write | revokeInvitation | Yes | Revokes a pending invitation, whether it is |
| write | removeMember | Yes | Removes an active member. The last non-expiring active administrator cannot be removed, and that refusal arrives as a membership-state conflict rather than a permission error. Removal also flips the connectors that person authorized for the organization to |
| read | listReferenceFindings | No | Names the documents whose citations need attention, one page of citing documents at a time, in an envelope of |
| write | planOrganizationDeletion | Yes | Records an immutable, short-lived deletion plan with a manifest and inventory totals. It quarantines and deletes nothing, so it is the safe way to see what a deletion would take. Takes a caller-minted UUIDv7 |
| write | applyOrganizationDeletion | Yes | The consequential boundary: it immediately fences ordinary writes, hides public content, and starts the reversible quarantine sweep. It takes the platform |
| write | cancelOrganizationDeletion | Yes | Reverses an applied deletion while the lifecycle is still deleting, restoring active writer access and superseding the remaining quarantine obligations. It becomes unavailable the moment teardown claims the lifecycle, so the receipt's |
| read | getConsequentialActionReceipt | No | Reads the durable deletion receipt: state, blockers, inventory totals, quarantine and teardown timing, restoration status, successor pointer, and |
| write | setOrganizationDeletionSuccessor | Yes | Sets or clears the optional successor organization while the deletion grace period is open; |
| write | planDocumentCopy | Yes | Plans one independent snapshot copy of a single document into another organization. Planning writes no target document. V1 accepts only a clean current Markdown or AGENTS.md document with portable metadata, empty settings, no content or post association, and no structural document reference; same-organization requests are rejected. |
| write | applyDocumentCopy | Yes | The confirmation boundary for a copy: it creates the new document under the destination organization's ordinary permissions. On a completed receipt, |
| read | getDocumentCopyReceipt | No | Reads a copy receipt after a lost response or for audit. It never runs, resumes, or expires the copy; there is no background copy pipeline. Each deliberate poll should omit |
- Method
- listMine
- Source Kind
- organization you are an active member of
Start here. Almost every organization-scoped method across every namespace takes an organizationId, and this is where those ids come from. Read result.organizations, not the result itself.
An organization that is not active refuses ordinary calls. Ordinary organization operations authorize live and then require an active organization, so once a deletion has been applied they, and every other namespace, fence with a lifecycle error until the deletion is cancelled; listMine, getMyAccess, and the deletion lifecycle methods remain callable subject to their own authorization checks and expose the lifecycle information needed for recovery. API-key principals cannot create organizations at any scope level, because a key's authority is the memberships it was provisioned into. There is no method to leave an organization, to change your own roles, to accept an invitation, or to reactivate a deleted organization: acceptance happens when a person opens the invite URL, and role and feature administration lives in the web app. renameOrganization changes only the display name; it moves no memberships, documents, or integrations. listMine reports integration names only, so use the integration's own listSources method for concrete accounts and lifecycle state. Membership rosters, the reference ledger, and AGENTS.md each sit behind a different permission, so a principal can legitimately see an organization in listMine while three of that row's fields are withheld. Private and nonexistent organizations are deliberately indistinguishable to an unauthorized caller.
Every list-like method returns an envelope, never a bare array. listMine and listMembers page by an opaque UUID cursor and answer hasMore; listMine defaults to 20 rows and allows 100, listMembers defaults to 100 and allows 1000. listReferenceFindings pages by citing document, defaults to 20 and allows 100, and also enforces a 96 KiB page budget, so a page of documents that each cite many drifted documents ends early with fewer rows than you asked for; hasMore and nextCursor continue correctly either way. Paginate until hasMore is false rather than assuming one page is the whole answer.
Two fields take a caller-minted UUIDv7: intentId on planOrganizationDeletion and the copy ids on the snapshot-copy methods. Generate them on your own host before the first attempt and keep the literals; call ids.mintOperationIds only if your host cannot generate a UUIDv7, in a run of its own where it must be the only provider call, then paste the returned literals into the mutation script. Reuse a value only for an exact retry of the same intent; a fresh value for the same intent creates a second, competing operation. The operationId on applyOrganizationDeletion, cancelOrganizationDeletion, getConsequentialActionReceipt, and applyDocumentCopy is the opposite kind of id: it is returned by the platform from the plan, and replacing it with a minted one is always wrong.
Invitation creation shares a UTC-day allowance with the web app: free organizations normally share their funding account's allowance of 5 for a new account or 50 for a verified account, with a fallback of 5 per organization when the responsible account is suspended or detached; an organization on the Pro plan or sponsored allows 50, and a free organization whose free credit is exhausted cannot invite. A repeat invitation to the same person is refused inside a 30 second resend cooldown. Each run carries a bounded provider-call budget and each target organization carries a throughput allowance, so batch reads deliberately instead of calling listMine once per loop iteration. Retry semantics differ by failure: a membership-state conflict means refresh with listMembers and decide again, an invitation whose email delivery outcome is unknown means the invitation may still be pending and must be inspected before any retry, and a deletion whose receipt says human_review_required or operator_intervention_required is not something a retry resolves.
Two planes constrain every call and both must permit it. The first is the delegated OAuth scope on your token: mcp:read is required for everything, and mcp:write for every mutation. The second is the organization plane, which is your live roles intersected with the organization's enabled features, expressed as permission strings. getMyAccess returns both planes for one organization and is the diagnostic for any denial.
Beyond membership, the concrete requirements are: org:sys_memberships:read for listMembers; an organization administrator role plus org:sys_memberships:create for inviteMember and plus org:sys_memberships:delete for revokeInvitation and removeMember; an administrator role plus org:sys_profile:manage for renameOrganization; an administrator role for setOrganizationVisibility; org:document_management:read for listReferenceFindings and for the referenceAttention field on a listMine row; and an administrator role plus org:sys_lifecycle:delete for the deletion lifecycle. createOrganization targets no existing organization, so it has no membership to consult and is gated on scope and authentication method alone: OAuth only, API keys refused. getMyAccess itself needs only mcp:read and an active membership, which is what makes it safe to call when you do not yet know what you may do.
The agentsMd field follows the document-read predicate rather than membership, so it can be present for an active public organization; referenceAttention stays membership-permission-only because it is not a public surface. A withheld agentsMd reports unavailable and deliberately does not reveal whether guidance exists.
This namespace is the entry point for the rest of the platform. listMine produces the organizationId that the documents, files, logs, and connector namespaces all take, and its agentsMd.documentId is the document to read through the documents namespace before doing organization work. listReferenceFindings hands back block keys and mark keys meant to be read with a block-scoped document read, and repaired by rewriting the citing block. The logs namespace records what happened in an organization, and orientation.whoami tells you which principal you are before any of this. The snapshot-copy methods are mounted here rather than in the documents namespace because a copy crosses an organization boundary, but everything they touch is a document.
Organizations
What it does
The organizations namespace is where agent work in Oddlynew begins. Organizations are the knowledge and access boundary: documents, files, integrations, and permissions all belong to one, and almost every other namespace takes an organizationId that comes from here. This namespace discovers those organizations, explains what you are allowed to do inside one, creates new ones, administers who belongs to them, reports which citations have gone stale, and drives the reversible deletion lifecycle. Authorization is checked live at every call, so an organization created earlier in the same script is usable by the next line.
Start here: listMine returns an envelope
organizations.listMine returns an object, not an array. Its shape is { organizations, hasMore, nextCursor? }, and the organization rows live under organizations. Calling .map on the result itself is the single most common way to crash a first script, and it fails with a type error that looks nothing like the real cause. Keep the envelope in a variable and read page.organizations from it; do not destructure it into a name that shadows the organizations namespace itself.
Only memberships that are currently active and unexpired produce a row, so an organization missing from the list is usually an expired or pending membership rather than a bug. Rows are ordered by organizationId and paged by a cursor that is itself an organization id; the default page is 20 rows and the maximum is 100. Pass query for a case-insensitive contains search on the name. Every row also carries a lifecycleStatus, which is active for a normal organization and one of deleting, quarantining, quarantined, tearing_down, or deleted for one that is being wound down. A non-active organization will refuse ordinary work, so check this before planning a long sequence of writes against it.
const page = await organizations.listMine({ limit: 50 })
// The envelope, not an array: page.organizations is the list.
const active = page.organizations.filter(o => o.lifecycleStatus === 'active')
// Paginate rather than assuming one page is everything.
let cursor = page.nextCursor
while (page.hasMore && cursor) {
const next = await organizations.listMine({ cursor, limit: 50 })
active.push(...next.organizations.filter(o => o.lifecycleStatus === 'active'))
cursor = next.nextCursor
if (!next.hasMore) break
}
const target = active.find(o => o.name === 'Oddlynew GmbH')
if (!target) throw new Error('Not a member of that organization')
return { organizationId: target.organizationId, agentsMd: target.agentsMd }The example shows the flow; live server validation inside run is authoritative for exact parameters.
AGENTS.md is the deterministic guidance entrypoint
Each listMine row carries an agentsMd field, and it exists so that finding an organization's operating instructions never requires a search. It is a tagged union with four states. available carries the exact documentId of the single active AGENTS.md, and you should read that document before doing substantive work in the organization. not_configured means the organization has none, and its recommendedAction suggests considering one. ambiguous means more than one document claims the role, and the field deliberately refuses to choose between competing guidance: consolidate them rather than picking one. unavailable means your principal lacks document-read permission there, and it is careful to reveal nothing about whether guidance exists.
Read the status field and branch on it. Do not treat a missing documentId as "no guidance", because three of the four states have no documentId for three different reasons, and only one of them means the organization actually lacks an AGENTS.md.
Why a call was denied: getMyAccess
When a call fails with a permission error, organizations.getMyAccess is the diagnostic. It answers for exactly one organization and returns four things: roles, your live role keys there; enabledFeatures, the features that organization has switched on; organizationPermissions, the permission strings those two produce together; and delegatedScopes, the OAuth scopes your token actually carries.
The reason it takes four fields to answer one question is that two independent planes constrain every call. Your token's scopes are one plane: without mcp:write no mutation succeeds, regardless of how senior your role is. The organization's permissions are the other: a permission exists only where a role grants it and the organization has the owning feature enabled, so an administrator in an organization with document management switched off genuinely cannot read the reference ledger. A denial means one plane refused, and comparing the two lists tells you which, and therefore whether the fix is reconnecting with a broader token or asking an administrator for a role.
const access = await organizations.getMyAccess({ organizationId })
const canWrite = access.delegatedScopes.includes('mcp:write')
const canInvite = access.organizationPermissions.includes('org:sys_memberships:create')
if (!canWrite) return 'Token plane: reconnect with a token granting mcp:write.'
if (!canInvite) return { reason: 'Organization plane', roles: access.roles, enabledFeatures: access.enabledFeatures }
return 'Both planes permit inviting.'The example shows the flow; live server validation inside run is authoritative for exact parameters.
Creating and shaping an organization
The method to create one is organizations.createOrganization. It is not organizations.create, and guessing the shorter name is a common first failure. It takes a name, makes you its administrator, and returns an organizationId that authorizes immediately: later calls in the same run script can use it, and no reconnection is required.
API-key principals cannot create organizations at all. A key's entire authority is the set of organizations it was provisioned into, so allowing it to found one it would then administer would let a narrow key widen itself. Creation is available only over a delegated OAuth connection, or to a person in the web app. If you are running as a key and need a new organization, that is a request to hand back to a human rather than a call to retry.
Once created, renameOrganization changes the display name and nothing else, and it rejects a name with surrounding whitespace rather than trimming it, so send exactly what should be stored. setOrganizationVisibility flips the organization between private and public with an explicit target, which makes retries idempotent. It is the one method whose organizationId may be omitted, and omitting it means your current default organization; when you have no default, the call fails and tells you to pass one explicitly, so prefer being explicit. A public-to-private flip returns a count of inbound cross-organization citations, masked to the source organizations you are entitled to see, which is your signal for how much other people's work is about to lose its reference.
Administering membership
listMembers returns active members and pending invitations together, in an envelope of { organizationId, lifecycleStatus, members, hasMore, nextCursor? }. Each row's status is the field that decides which removal method applies: invited and provisioned rows are revoked with revokeInvitation, and active rows are removed with removeMember. Using the wrong one fails rather than doing the other thing. Rosters carry email addresses and phone numbers, so the method requires the same permission the product's team page enforces rather than bare membership.
inviteMember creates the invitation and, unless you pass notify: false, sends the email. In every case it returns a single-use inviteUrl. Treat that URL as a credential: anyone holding it can accept the membership, so it does not belong in a document, a log line, or a summary you hand back to a wide audience. Note that expiresAt on the input is when the granted membership access expires, which is a different thing from the invite link's own thirty day lifetime.
Two failure modes deserve care. If email delivery is refused, the invitation is rolled back automatically, and the error says whether that rollback actually completed; when it did not, inspect listMembers before inviting again so you do not create a duplicate. If the delivery outcome is unknown, nothing is rolled back and the invitation stays pending: do not retry automatically, because the email may well have been sent. Separately, an organization may create at most 50 invitations per UTC day, and re-inviting the same person inside a 30 second cooldown is refused; both are shared with the web app, so an agent and a person working the same organization draw on one budget.
Removals have their own guard: the last non-expiring active administrator cannot be removed, and the attempt comes back as a membership-state conflict telling you to refresh and reconsider. Removing an active member also flips the organization connectors that person authorized to needs_reauth, which is honest rather than broken: the organization sees a remediable status immediately instead of a connector that silently stops working.
const roster = await organizations.listMembers({ organizationId })
const stale = roster.members.filter(m => m.status === 'invited')
for (const invite of stale) {
await organizations.revokeInvitation({ organizationId, membershipId: invite.membershipId })
}
const invited = await organizations.inviteMember({
organizationId,
email: 'new.colleague@example.com',
invitedName: 'New Colleague',
roleKey: 'member',
})
// inviteUrl is a single-use credential. Report that it was sent, not the URL.
return { membershipId: invited.membershipId, emailSent: invited.emailSent }The example shows the flow; live server validation inside run is authoritative for exact parameters.
Connections, API keys and capacity
Connections. listConnections({ organizationId }) lists the organization's shared connections (Google, Meta, MCP servers, APIs and other connected services), each with the run namespace that calls it and its connectorId, which you pass to that namespace as sourceId. Call it before you reach for a provider key, a shell or another connector. Each row says whether the connection is active or needs_reauth, whether it is paused or needs a scope upgrade, its confirmation policy and who authorized it, and carries pageUrl, the web page where a person reconnects or manages it. Connecting and reconnecting need a person's consent in the browser, so when a connection needs attention, hand pageUrl to an organization administrator. It needs mcp:read and the permission to use the organization's connections.
An Admin's agent can also change a connection. renameConnection changes only its display label. setConnectionNote sets or clears a note that everyone who sees the connection reads; it stays when the connection is reconnected. pauseConnection stops everyone, agents included, from using the connection until a person resumes it on its web page; the provider authorization stays, so resuming needs no new sign-in. Resuming and disconnecting are done on the web page only.
API keys. listApiKeys({ organizationId }) lists the organization's keys that are not revoked, with metadata only: name, label, kind (a shared agent's key or a Content API site key), display prefix, roles, and when it was created, expires and was last used. Expired keys are listed too, so compare expiresAt with the current time; a null expiresAt never expires. No secret is ever returned, and keys are created by a person on the web page, never here. revokeApiKey revokes one at once and cannot be undone; the public API may still accept the key for up to about 75 seconds. A member's personal key is never in scope. Both need an organization administrator with permission to manage keys; revoking also needs mcp:write.
Capacity. getCapacity({ organizationId }) reads what the organization's billing page shows: its plan, the documents it holds against its limit, and how much input, read and search capacity is left and when it refills. Call it before bulk work instead of learning the limit from a refusal. It is read-only, never returns payment details, and answers organization administrators only.
Citation health: referenceAttention and listReferenceFindings
Documents in Oddlynew cite each other, and citations go stale. Each listMine row carries a referenceAttention summary, and listReferenceFindings turns those counts into the documents involved.
The summary's most important property is that zero never means healthy. Read structuralCoverage first: unknown means nothing has been verified for that organization yet, partial means at least one cited document is still awaiting verification, and only complete means every cited document carried a full proof when the row was built. Alongside it, pendingTargets and oldestPendingAt say how much verification is still owed, counting both cited documents awaiting a scan and your own changed documents the verifier has not reached. A row of zeros under unknown coverage says nothing has been checked, not that everything is fine. The whole field is null when nothing has been verified yet, and also when you lack org:document_management:read there, which are two very different situations that look identical from outside. Comment threads never enter these counts: a comment anchors the passage it discusses, and that anchor drifting is the comment's own lifecycle.
listReferenceFindings takes a required bucket. confirmed means the citation no longer resolves, because the cited block, the pinned version, or the whole cited document is gone. review means the cited block still resolves but its content changed, so a person or agent has to judge whether the citation still holds. Findings are paged by citing document, and each finding lists targets with the exact basis they were proved at and occurrences giving the block key, mark key, anchor text, and what the citation pins. That is enough to open the passage and compare it against the cited block as it is now.
Repair differs by bucket. A review citation that still holds is re-cited by rewriting its block unchanged: the citation re-pins to the current content and the finding clears on the next scan. One that no longer holds is reworded. A confirmed citation points at something that is gone, so an unchanged rewrite is rejected outright; reword the passage or point the citation at whatever replaced it.
Three fields on the envelope are easy to misread. truncated says this call left verification work behind and calling again makes more progress; it never means the queue is drained, and coverage is what knows that. paused means an operator has paused the ledger entirely, so the counts are from the last pass before the pause. And occurrences is read live beside the finding rather than recorded by the scan, so an empty occurrence list means you already did the work and the ledger has not caught up. Note also that only a call without a cursor does verification work; paging deeper reports the coverage the first page earned, because one walk is one act of looking.
const page = await organizations.listReferenceFindings({ organizationId, bucket: 'confirmed' })
// Zero findings is not a clean bill under partial coverage.
if (page.findings.length === 0 && page.coverage.structuralCoverage !== 'complete') {
return { verdict: 'unproven', coverage: page.coverage, truncated: page.truncated }
}
const work = page.findings.flatMap(f =>
f.targets.flatMap(t =>
t.occurrences.map(o => ({
document: f.sourceDocumentId,
block: o.sourceBlockKey,
cites: t.targetDocumentId,
readable: t.readable,
preview: o.anchorText,
})),
),
)
return { work, hasMore: page.hasMore, coverage: page.coverage }The example shows the flow; live server validation inside run is authoritative for exact parameters.
Deleting an organization
Deletion is a three step lifecycle with a reversible middle, and the receipt is the state machine you follow. planOrganizationDeletion records an immutable, short-lived plan with a manifest and inventory totals, and changes nothing about the organization, so it is the safe way to see the scale of what a deletion would take and which blockers stand in the way. It takes a caller-minted UUIDv7 intentId; generate it on your own host and keep the literal, or with ids.mintOperationIds in a run of its own when the host cannot, where minting must be that run's only provider call, and reuse the same literal only when retrying that same plan. Reusing an intentId for a different organization is refused.
applyOrganizationDeletion is the consequential boundary. It takes the platform operationId from the plan receipt and the planToken passed through unchanged, and it must be called by the same user who created the plan. There is no separate confirmation step in v1; the receipt simply records that approval was satisfied without one. The moment it succeeds, ordinary writes are fenced, public content is hidden, and the quarantine connection sweep begins. From then on every other namespace, and every ordinary method here, refuses that organization with a lifecycle error until the deletion is cancelled.
Quarantine is reversible. cancelOrganizationDeletion restores active writer access and supersedes the remaining obligations, but only while the lifecycle is still deleting; once teardown claims it, cancellation is gone. getConsequentialActionReceipt reads the durable record without advancing anything, and its nextAction is the field to obey: it distinguishes apply, wait_for_quarantine, cancel_or_wait_for_teardown, wait_for_restoration, wait_for_teardown, create_new_plan, done, and the two states that no retry resolves, human_review_required and operator_intervention_required. Polling the receipt will never move a stuck deletion forward. Optionally, setOrganizationDeletionSuccessor records where the work is moving; that pointer survives onto the public tombstone, and passing null clears it.
// intentId comes from a prior ids.mintOperationIds run, pasted as a literal.
const intentId = '0198f1d2-6c40-7a11-9f3a-1d2e4c5b6a70'
const plan = await organizations.planOrganizationDeletion({ organizationId, intentId })
// Planning changed nothing. Inspect before crossing the boundary.
if (plan.blockers.length > 0) return { blockers: plan.blockers, nextAction: plan.nextAction }
if (plan.nextAction !== 'apply') return { nextAction: plan.nextAction, state: plan.state }
const applied = await organizations.applyOrganizationDeletion({
organizationId,
operationId: plan.operationId,
planToken: plan.planToken,
})
return { state: applied.state, nextAction: applied.nextAction, inventory: applied.inventoryTotals }The example shows the flow; live server validation inside run is authoritative for exact parameters.
For discovery, ask guide a direct question such as How do I invite someone to an organization?. For exact parameters, live server validation inside run is authoritative.