Developers
Recipes
Both directions, end to end.
Give your ARK to your AIEarly access
Make an owner key holding read:records, write:proposals and the scopes ask_ark and get_ark_profile accept (listed below). Connect your MCP client to the server with it. Your AI can then ask the ARK and propose records from your conversations; each one waits for you in the proposals panel.
| Action | Scopes (any one) |
|---|---|
ask_ark | generate:text, generate:chat, generate:email, generate:social |
get_ark_profile | read:profile, read:stats |
You are connected to my ARK. You may propose records to it; I review every one.
Label authorship honestly: self_verbatim only for my exact words, self_paraphrase when you restate me, about_subject for anything someone else said about me.
Never invent words and label them verbatim. Never tidy or translate a verbatim quote.
Mark third-party material about_subject.
Never send voice records.
Prefer one clear record per idea, and tell me what you proposed.Import from a platformEarly access
A source key, a transcript sent on the raw lane and typed records sent in batches. A failed batch is resent as is: dedupe answers duplicate for anything already received. Then the script polls the status check and reports what the owner accepted.
// node import.mjs envelopes.json (each envelope with provenance.content_sha256; see Writing into an ARK)
import { readFileSync } from 'node:fs'
const API = 'https://www.arkz.ai/api/human'
const KEY = process.env.ARKZ_SOURCE_KEY
const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
const call = (action, params) => fetch(API, {
method: 'POST',
headers: { Authorization: 'Bearer ' + KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ action, ...params }),
}).then((r) => (r.status === 429 || r.status >= 500 ? Promise.reject(new Error('retry ' + r.status)) : r.json()))
// Retry with backoff; safe for proposals because a resend answers duplicate.
const withRetry = (fn, wait = 2000, tries = 6) =>
fn().catch((e) => (tries <= 1 ? Promise.reject(e) : sleep(wait).then(() => withRetry(fn, wait * 2, tries - 1))))
// Ask about the open ids in batches of 100, backing off between rounds.
async function waitForDecisions(ids, rounds = 12) {
const decided = new Map()
let wait = 30_000
for (let round = 0; round < rounds && decided.size < ids.length; round++) {
const open = ids.filter((id) => !decided.has(id))
for (let i = 0; i < open.length; i += 100) {
const answer = await withRetry(() => call('get_ark_proposal_status', { proposal_ids: open.slice(i, i + 100) }))
for (const p of answer.proposals) if (p.status !== 'proposed' && p.status !== 'held') decided.set(p.proposal_id, p.status)
}
if (decided.size < ids.length) { await sleep(wait); wait = Math.min(wait * 2, 6 * 3600 * 1000) }
}
console.log([...decided].filter(([, s]) => s === 'accepted').length, 'accepted of', ids.length)
}
const envelopes = JSON.parse(readFileSync(process.argv[2], 'utf8'))
const ids = []
for (let i = 0; i < envelopes.length; i += 100) {
const answer = await withRetry(() => call('propose_ark_records', { envelopes: envelopes.slice(i, i + 100) }))
for (const r of answer.results) {
if (r.proposal_id) ids.push(r.proposal_id)
else if (r.status === 'duplicate') ids.push(r.duplicate_of)
else console.warn('refused', r.index, r.error, r.message)
}
}
await waitForDecisions(ids)A derivative behind your own agent
Your website or app agent holds a credential bound to a derivative it owns or licenses and relays each visitor message to ask_ark. ask_ark carries no history, so the agent folds the recent turns into one question within the length cap.
const API = 'https://www.arkz.ai/api/human'
const KEY = process.env.ARKZ_DERIVATIVE_KEY
const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
const call = (action, params) => fetch(API, {
method: 'POST',
headers: { Authorization: 'Bearer ' + KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ action, ...params }),
}).then((r) => (r.status === 429 || r.status >= 500 ? Promise.reject(new Error('retry ' + r.status)) : r.json()))
// Retry with backoff; safe for proposals because a resend answers duplicate.
const withRetry = (fn, wait = 2000, tries = 6) =>
fn().catch((e) => (tries <= 1 ? Promise.reject(e) : sleep(wait).then(() => withRetry(fn, wait * 2, tries - 1))))
const MAX_QUESTION = 4000
// ask_ark carries no history: fold the recent turns into one question, keeping the end.
const pack = (history, message) => (history.map((t) => t.role + ': ' + t.text).join('\n') +
'\n\nThe visitor now asks: ' + message).slice(-MAX_QUESTION)
const relay = (history, message) => call('ask_ark', { content: pack(history, message) }).then((a) => a.answer)Read onlyEarly access
An app that reads a licensed ARK and writes nothing: a licence credential, list_ark_records to page through the readable records, get_ark_profile for the profile, ask_ark for answers billed to the licensee under the licence. It never calls a propose tool.
The loopEarly access
Read provenance to see where the ARK knowledge came from, propose what is missing, wait for the owner to accept, then read the record back.
const API = 'https://www.arkz.ai/api/human'
const KEY = process.env.ARKZ_KEY
const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
const call = (action, params) => fetch(API, {
method: 'POST',
headers: { Authorization: 'Bearer ' + KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ action, ...params }),
}).then((r) => (r.status === 429 || r.status >= 500 ? Promise.reject(new Error('retry ' + r.status)) : r.json()))
// Retry with backoff; safe for proposals because a resend answers duplicate.
const withRetry = (fn, wait = 2000, tries = 6) =>
fn().catch((e) => (tries <= 1 ? Promise.reject(e) : sleep(wait).then(() => withRetry(fn, wait * 2, tries - 1))))
const provenance = await call('get_ark_provenance', {})
console.log(provenance)
const proposed = await withRetry(() => call('propose_ark_record', { envelope: JSON.parse(process.argv[2]) }))
console.log(proposed.proposal_id ?? proposed.duplicate_of, proposed.status)
// The owner accepts it in the proposals panel. Then:
const page = await call('list_ark_records', { limit: 10 })
console.log(page)