Connections

Organization API

Connect trusted systems to member, membership, payment, publication, and access data with scoped keys and a focused HTTP API.

Audience
Integration developers and organization owners
Reading time
12 minute read

Before you begin

What you’ll need

  • Permission to manage connected-system keys
  • Connected services available for your organization
  • A recent secure identity check to manage keys
  • A server or trusted backend that can keep credentials private
01

Create a key for a trusted system

The Organization API is a server-to-server HTTP interface for one organization. A system key determines the organization and allowed operations; requests do not choose an organization ID.

  1. Open Organization settings → Security & API → Keys for trusted systems and select Create system key.
  2. Give the key a recognizable name, choose only the access the system needs, and optionally set an expiration date.
  3. Select Create and reveal key once. Store the full key in your backend's secret store, then select I stored this key.
  4. Use Replace key when rotating credentials, or Disable to stop a system's access. Replacement immediately disables the previous key.
02

Make your first request

Use the configured Supabase project URL followed by /functions/v1/organization-api. An owner can find the project's host in the endpoint shown under Organization agents; replace the final organization-mcp segment with organization-api. The portal website URL is not the API base URL.

  • Every request needs Authorization: Bearer <system-key> and X-Request-ID containing a UUID. Generate a fresh request ID for each HTTP attempt and retain it for troubleshooting.
  • Writes also need Content-Type: application/json and Idempotency-Key with 8–200 printable characters. The idempotency key identifies the intended change and stays the same on retries.
  • The examples use shell variables. Set API_BASE to your real API base URL and load SYSTEM_KEY securely before running them. Replace example record IDs with IDs from your organization's responses.
Read the first page of membersShell
API_BASE="https://<project>.supabase.co/functions/v1/organization-api"

# SYSTEM_KEY is supplied by your secret store.
curl --fail-with-body "$API_BASE/members?limit=50" \
  -H "Authorization: Bearer $SYSTEM_KEY" \
  -H "X-Request-ID: $(uuidgen)"
03

Routes and required scopes

Paths below are relative to the API base URL. Read and write scopes are independent: a write scope does not automatically provide read access. Only these routes are currently supported.

Organization API route reference
Method and pathRequired scopeResult
GET /membersmembers:readMember profiles, primary email and phone, preferences, and custom fields.
GET /membershipsmemberships:readMemberships, option names, status, dates, and paid-through dates.
GET /paymentspayments:readPayment history, status, currency, amounts, and receipt status.
GET /publicationspublications:readPublication issues, release information, and asset metadata; no protected download URLs.
GET /entitlementsentitlements:readStored benefit-access records, effects, resources, and validity dates.
PATCH /members/{member_record_id}members:writeUpdate the supported fields on an existing member profile.
POST /entitlements/overridesentitlements:writeCreate a documented one-member access override.
04

Read collections and follow cursors

All five GET routes accept limit (1–200; default 100) and an optional UUID cursor. Results are ordered by record ID. Other query parameters, duplicate parameters, and GET request bodies are rejected.

  • Process items, then send next_cursor as cursor on the next request. Stop when next_cursor is null. A full final page can be followed by one empty page.
  • There is no search, date filter, updated-since filter, or individual-record GET route in this API. Select records from collection results in your integration.
  • Read the currency alongside every amount ending in _minor. Those values are integer minor units, not formatted decimal amounts.
  • Publication results describe files but do not provide authorization to download protected content. Keep member and payment data within the systems approved by your organization.
Collection response envelope (empty page)JSON
{
  "collection": "members",
  "items": [],
  "next_cursor": null,
  "request_id": "7f90ee4c-4fb5-4b64-a7d1-a7fc72d6a280"
}
05

Update a member profile

PATCH /members/{member_record_id} accepts a nonempty JSON object containing only the fields below. Send the changes directly in the body, without a changes wrapper. Omitted fields stay unchanged.

Supported member update fields
FieldAccepted value
display_nameNonblank string, up to 200 characters.
legal_nameNonblank string, up to 300 characters; null clears it.
member_numberNonblank string, up to 100 characters; null clears it.
statusprospective, pending, active, inactive, deceased, rejected, or archived. This is the member profile's status, not a membership's status.
delivery_preferences · communication_preferences · directory_preferences · custom_fieldsJSON objects. Each supplied object replaces that field's current object; nested values are not merged.
Change a display nameShell
MEMBER_ID="<member-record-uuid>"
OPERATION_KEY="<unique-key-for-this-change>"

curl --fail-with-body -X PATCH "$API_BASE/members/$MEMBER_ID" \
  -H "Authorization: Bearer $SYSTEM_KEY" \
  -H "X-Request-ID: $(uuidgen)" \
  -H "Idempotency-Key: $OPERATION_KEY" \
  -H "Content-Type: application/json" \
  --data '{"display_name":"Alex Morgan"}'
Successful member updateJSON
{
  "result": {
    "command": "member.update",
    "member_record_id": "b6a4e468-f514-4f5c-934e-2cf50724a490",
    "updated": true,
    "replayed": false
  },
  "request_id": "7f90ee4c-4fb5-4b64-a7d1-a7fc72d6a280"
}
06

Create a one-member access override

POST /entitlements/overrides requires member_record_id and resource_id UUIDs, effect (grant or deny), and a reason of 3–2,000 characters. Both records must belong to the key's organization, and the resource must be active.

  • valid_from is optional and defaults to the current time. valid_until is optional or null for no end date; if supplied, it must be later than the start.
  • Supply timestamps in ISO 8601 format with Z or a numeric timezone offset. Use a resource_id from an existing access record or the organization's authorized benefit configuration.
  • The body limit is 16 KiB. The response uses the result and request_id envelope, with command set to entitlement.override.
Override request bodyJSON
{
  "member_record_id": "b6a4e468-f514-4f5c-934e-2cf50724a490",
  "resource_id": "6e16ce34-cff4-42f9-a47e-7079af6b75ab",
  "effect": "grant",
  "reason": "Approved access exception for this member",
  "valid_until": null
}
07

Handle errors, limits, and retries

Successful reads and writes return HTTP 200. Errors contain error.code, error.message, and request_id. Record the status and request ID for support without logging the Authorization header or private request bodies.

  • The current budget is 120 requests per minute per key. Authorized responses include RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset (an ISO timestamp); throttled responses include Retry-After in seconds.
  • On a timeout or uncertain write result, retry with the same credential, Idempotency-Key, and identical body. A completed replay returns result.replayed: true without applying the change again. Use a new X-Request-ID for the HTTP attempt.
  • Write retry keys are scoped to the credential. Do not assume a retry after replacing a credential is protected by the old key's history; inspect the result before submitting through a new credential.
HTTP outcomes and recovery
StatusWhat to check
400Required UUID X-Request-ID, write Idempotency-Key, valid JSON, supported fields, and pagination parameters.
401Missing, malformed, replaced, disabled, or expired system key.
403The key's scope and current authorization for the requested operation.
404Unsupported route/method or a record unavailable to this organization.
409Request conflict, including reuse of a write key with different arguments. Resolve the conflict before retrying.
413 / 415Request body too large / missing or unsupported Content-Type. Writes require application/json.
429Rate limit reached. Wait for the Retry-After interval before retrying.
503Temporarily unavailable. Retry with bounded backoff; preserve the write's original key and body.
Example validation errorJSON
{
  "error": {
    "code": "invalid_request_id",
    "message": "X-Request-ID must be a UUID."
  },
  "request_id": "7f90ee4c-4fb5-4b64-a7d1-a7fc72d6a280"
}