LibraryOrganizations Namespacev8 · Updated Report

Organizations Namespace

Integration Identityorganizations · core
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.

Integration Capability18
AccessMethodSide EffectDescription
readlistMineNo

Returns an envelope, { organizations, hasMore, nextCursor? }, never a bare array: reach for result.organizations before mapping. Each row carries organizationId, name, lifecycleStatus, integration names, the agentsMd guidance entrypoint, and referenceAttention. Only organizations where your membership is active and unexpired appear.

readgetMyAccessNo

The answer to "why was I denied". Returns your live roles, the organization's enabledFeatures, the organizationPermissions those two produce together, and the delegatedScopes on your token. Both planes constrain every call, so a permission present in one and absent in the other still denies.

writecreateOrganizationYes

The method is called createOrganization, not create. You become its administrator and the returned organizationId is usable immediately, in the same run script and on later requests, with no reconnect. API-key principals are refused outright: creation is available only on a delegated OAuth connection.

writerenameOrganizationYes

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 org:sys_profile:manage permission.

writesetOrganizationVisibilityYes

Sets one organization private or public. This is the only method whose organizationId may be omitted, and omitting it means "my current default organization", which fails when there is no default. Retries with the same explicit target are idempotent. A public-to-private flip reports only the inbound cross-organization citations whose source organizations you can read.

readlistMembersNo

Lists active members and pending invitations together in one envelope, { organizationId, lifecycleStatus, members, hasMore, nextCursor? }. Member statuses are active, invited, and provisioned, and status is what tells revokeInvitation apart from removeMember. Rosters carry personal data, so this needs org:sys_memberships:read, not bare membership.

writeinviteMemberYes

Creates the invitation and, by default, sends the email. The role is reader (Reader), member (Writer) or org_admin (Admin). It always returns a single-use inviteUrl: that URL is a credential, so treat it as a secret and never write it into a document. notify: false still creates the invitation and returns the URL. Requires an organization administrator role and org:sys_memberships:create.

writerevokeInvitationYes

Revokes a pending invitation, whether it is invited or provisioned. Calling it on an active member fails; use removeMember for those. Requires an organization administrator role and org:sys_memberships:delete.

writeremoveMemberYes

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 needs_reauth.

readlistReferenceFindingsNo

Names the documents whose citations need attention, one page of citing documents at a time, in an envelope of { findings, hasMore, nextCursor?, coverage, truncated, paused }. bucket is required and picks the question: confirmed means the citation no longer resolves, review means the cited block changed and someone has to judge it. An empty page under partial coverage is not a clean bill of health.

writeplanOrganizationDeletionYes

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 intentId and returns the planToken that applyOrganizationDeletion demands. Requires org:sys_lifecycle:delete.

writeapplyOrganizationDeletionYes

The consequential boundary: it immediately fences ordinary writes, hides public content, and starts the reversible quarantine sweep. It takes the platform operationId and the unchanged planToken from the plan receipt, the plan must be ready and unexpired, and only the user who created the plan may apply it. No separate confirmation step exists in v1.

writecancelOrganizationDeletionYes

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 nextAction is what tells you whether cancelling is still on the table.

readgetConsequentialActionReceiptNo

Reads the durable deletion receipt: state, blockers, inventory totals, quarantine and teardown timing, restoration status, successor pointer, and nextAction. It never advances work, so polling it will not move a stuck deletion forward.

writesetOrganizationDeletionSuccessorYes

Sets or clears the optional successor organization while the deletion grace period is open; null clears it. The pointer is published on the terminal tombstone, so it is how readers of a deleted organization find where the work moved.

writeplanDocumentCopyYes

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.

writeapplyDocumentCopyYes

The confirmation boundary for a copy: it creates the new document under the destination organization's ordinary permissions. On a completed receipt, destination.documentId is the new document's stable id. Each request makes at most one idempotent creation attempt, so it is safe to retry after a timeout with the same operationId and planToken.

readgetDocumentCopyReceiptNo

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 readOperationId so the read mints its own; pass an echoed value back only for an exact transport retry. An applying receipt means wait until retryAfter and retry applyDocumentCopy, not poll harder.

Integration Source DiscoverylistMine · organization you are an active member of
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.

Limitations

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.

Usage

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.

Authorization

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.

Composition

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.

Oddlynew PublicSign in