Developers
Authentication
Every call carries one credential. Send it as Authorization: Bearer <credential>; an owner key may also go in the x-ark-api-key header. A credential is bound to one ARK.
The four credential kinds
| Kind | Prefix | Can read | Can write | Who makes it |
|---|---|---|---|---|
owner_key | ark_live_ | yes | yes | The ARK owner, on the API keys settings page: arp_create_owner_key_v1 when read:records or write:proposals is chosen, create_api_key for the eleven earlier scopes. Writing needs write:proposals. |
oauth_credential | arka_ | yes | yes | An app the owner connects on the consent screen (ark-oauth, PKCE S256). One connection per app; writing needs write:proposals. |
licence | lic_ | yes | no | Issued to a licensee of the ARK. It calls the actions its licence scopes allow and can never propose (grant_scope). |
source_key | ark_src_ | no | yes | The ARK owner, for one grant, in the Integration Hub grants panel. Write only: propose_ark_record, propose_ark_records and get_ark_proposal_status; every other action is source_key_forbidden. |
Owner keys
An owner makes an owner key on the API keys settings page for one ARK they own. The scopes ticked at creation decide what it can call; the table below lists the scopes each action accepts (any one of them is enough). A key can be revoked on the same page at any time; rotation is a new key, then revoking the old one.
Scopes per action
| Action | Availability | Scopes (any one) | Source key |
|---|---|---|---|
get_ark_profile | Live | read:profile, read:stats | no |
search_ark_topics | Live | read:profile, read:stats | no |
ask_ark | Live | generate:text, generate:chat, generate:email, generate:social | no |
health | Live | none | no |
get_ark_schema | Early access | none | no |
list_ark_records | Early access | read:records | no |
get_ark_record | Early access | read:records | no |
get_ark_provenance | Early access | read:records | no |
propose_ark_record | Early access | write:proposals | yes |
propose_ark_records | Early access | write:proposals | yes |
get_ark_proposal_status | Early access | write:proposals | yes |
Limits on a credential
| Limit | Value | Unit | Defined in |
|---|---|---|---|
source_key_calls | 30 | calls per 60 s per source key (approximate: the limiter is in memory per instance) | supabase/functions/_shared/arp/auth.ts:31 |
owner_key_calls | 30 | calls per 60 s per owner key (the default and the most a new key may set; approximate, in memory per instance) | supabase/migrations/20261215110000_arp_p1_keys_and_ledger.sql:726 |
owner_key_daily_calls | 1,000 | calls per 24 h per owner key (the default and the most a new key may set) | supabase/migrations/20261215110000_arp_p1_keys_and_ledger.sql:726 |
source_key_daily_calls | 1,000 | calls per rolling 24 h per source key (rate_limited beyond it; the window starts at the first call) | supabase/migrations/20261215110000_arp_p1_keys_and_ledger.sql:795 |
grant_proposal_quota | 200 | proposals per UTC calendar date per grant (a quota, not a call limit; the owner may set 1 to 5000) | supabase/migrations/20261215110000_arp_p1_keys_and_ledger.sql:41 |
credential_proposal_quota | 2,000 | proposals per UTC calendar date per owner key or OAuth connection without a grant (a quota, not a call limit) | supabase/migrations/20261216100000_arp_p2_proposals.sql:251 |
proposal_batch_max | 100 | envelopes per propose_ark_records call | supabase/functions/_shared/arp/proposals.ts:PROPOSAL_BATCH_MAX |
proposal_text_max | 200,000 | characters of content.text on the raw and voice lanes | supabase/functions/_shared/arp/proposals.ts:PROPOSAL_TEXT_MAX |
proposal_text_max_typed | 11,782 | characters of content.text on the typed lane (fence markers inside the text count double) | supabase/functions/_shared/arp/proposals.ts:PROPOSAL_TEXT_MAX_TYPED |
proposal_structured_max | 32,768 | bytes of content.structured as JCS, UTF-8 | supabase/functions/_shared/arp/proposals.ts:PROPOSAL_STRUCTURED_MAX_BYTES |
proposal_list_max | 50 | entries in domains, topics or lineage.derived_from | supabase/functions/_shared/arp/proposals.ts:PROPOSAL_LIST_MAX |
proposal_list_entry_max | 200 | characters per entry in those lists | supabase/functions/_shared/arp/proposals.ts:PROPOSAL_LIST_ENTRY_MAX |
status_ids_max | 100 | proposal ids per get_ark_proposal_status call | supabase/functions/_shared/arp/status.ts:STATUS_IDS_MAX |
status_page_max | 100 | proposals per status listing page | supabase/functions/_shared/arp/status.ts:STATUS_LIMIT_MAX |
records_page_max | 100 | records per list_ark_records page | supabase/functions/_shared/arp/gateway.ts:ARP_MCP_TOOLS list_ark_records limit.maximum |
scope_record_cap | 10,000 | records one scope may resolve to (arp_scope_too_large beyond it) | supabase/migrations/20261217100000_arp_p3_scope.sql:681 |
ask_question_max | 4,000 | characters per ask_ark question | supabase/functions/_shared/ark-actions.ts:askArk |
mcp_body_max | 64 | KiB per MCP request body | supabase/functions/ark-mcp/index.ts:68 |
mcp_batch_max | 20 | JSON-RPC messages per MCP batch | supabase/functions/ark-mcp/index.ts:375 |
proposal_expiry | 90 | d after creation a proposal not yet decided expires (expires_at); the expiry job, arp_expire_proposals_v1, is not scheduled yet, so nothing expires today | supabase/migrations/20261216100000_arp_p2_proposals.sql:58 |
rejected_retention | 30 | d after the decision a rejected proposal is removed (by the same unscheduled job) | supabase/migrations/20261216100000_arp_p2_proposals.sql:976 |
expired_retention | 30 | d after expires_at an expired proposal is removed (by the same unscheduled job) | supabase/migrations/20261216100000_arp_p2_proposals.sql:977 |
Source keysEarly access
A source key belongs to a source grant: a named platform the owner trusts to write into one ARK. It is write only. It can call the propose tools and the status check and nothing else; every other action refuses it, and it cannot open an MCP session (initialize and tools/list answer -32003), so a source uses REST or bare tools/call messages. It is limited to 30 calls per 60 s and 1000 calls per 24 h. The grant carries the allowlists of facets and kinds it may propose, its own daily proposal quota and its verification state. Keys rotate under the grant, and pausing the grant stops every key at once.
OAuth 2.1
An app that acts for an owner uses OAuth 2.1 with dynamic client registration and PKCE (S256). The authorization request names the resource indicator below. The owner sees a consent screen listing the scopes the app asked for and approves or declines. Access credentials are refreshed with rotating refresh credentials: each refresh credential works once.
| Field | Value |
|---|---|
| Discovery documents |
|
| Resource indicator | https://www.arkz.ai/mcp |
| PKCE method | S256 |
| Scopes the consent screen offers | read:profile, read:stats, read:usage, generate:text, generate:chat, generate:email, generate:social, generate:voice, generate:video, generate:image, verify:content, read:records, write:proposals |
Licence credentials
A licensee who holds an active licence for an ARK from the marketplace makes a licence credential for it and uses it like a key. It reads within the licence: the licence decides the scopes and the facets it opens, and the credential stops working when the licence ends. ARKZ publishes no separate licensing page today.