Skip to content

Calls, events and orgs

let answer = try await client.call(app, "send_message", ["message": "hi"]) // POST /apps/{app}/intents
answer.index; answer.result; answer.events
let read = try await client.view(app, "get_messages", [:]) // POST /apps/{app}/views
  • A call carries an idempotency key (node:c-… unless you pass one). While the node answers 503 or cannot be reached, the same request with the same key is sent again: 6 retries, 200 ms doubling to 2 s, or the node’s Retry-After when longer (CloudRetryOptions). Then UnavailableError, whose committed is .no or .unknown.
  • An app’s own refusal is AppError with appCode and fields.
  • callJSON / viewJSON take and return JSON bytes, every number as written (a u64 beyond 2^53); CloudContextContract uses them.
for try await event in client.events(app, from: 0) { … } // GET /apps/{app}/events, sealed SSE

Each event has id (index:seq), kind and data. Cancel the task (or stop iterating) to close the stream.

Call Route
orgs.create(name:recoveryKey:), orgs.get(org:), orgs.metadata(org:metadata:) /orgs, /orgs/{org}
setRecoveryKey(org, publicKey:, passkey:), startRecovery(org, seed:), orgs.recoveryCancel(org:passkey:), orgs.recoveryFinish(org:passkey:) /orgs/{org}/recovery…
orgs.teamCreate(org:name:join:), orgs.teamGet, updateTeam, orgs.teamDelete /orgs/{org}/teams/…
orgs.setMember, orgs.removeMember, orgs.teamSetMember, orgs.teamRemoveMember, orgs.capabilities (each with passkey:) /orgs/{org}/members, …/capabilities
orgs.inviteCreate(org:role:teams:passkey:) → .token (inv_…) POST /orgs/{org}/invites
orgs.inviteRedeem(token:) POST /invites/{token}/redeem
apps.create(name:package:org:access:) (access: AppAccess.team(id).generated) POST /apps

auth.me() lists the account’s organizations; orgs.get(org:) answers the organization, its teams, members and their metadata; OrgInfo(json: try JSONValue(encoding: org)) reads it as MeroKit’s type.

An owner who loses every passkey gets the organization back with its recovery seed (core rc.9+). The seed never leaves the device: the node stores only its public key and checks a signature the seed makes for the recovering account.

let seed = RecoverySeed()
showToOwner(seed.text) // 64 hex digits in groups of eight; keep it offline
let org = try await client.orgs.create(name: "Acme", recoveryKey: seed.publicKey).orgId
// Every passkey lost: sign up again with a new passkey, then
let times = try await fresh.startRecovery(org, seed: try RecoverySeed(text: written))
// 72 hours later (the owner may orgs.recoveryCancel; members see OrgInfo.recovery?.pending)
_ = try await fresh.orgs.recoveryFinish(org: org, passkey: newPasskey) // RecoveryNotReady before readyAt

Rotating the key (setRecoveryKey) cancels a recovery under way, and the old seed is refused from then on.

apps.blobUpload(app:body:contentType:) (the node stores the blob with the contentType you name and answers downloads with it; leave it out and it is sent as application/octet-stream), apps.blobDownload(app:id:grant:) (grant: no session, a grant one of the app’s runs handed out), blobContent(app, id, grant:) (bytes and content type), listBlobs, blobInfo, apps.blobDelete(app:id:) on /apps/{app}/blobs; uploadFile(app, data) for a component app’s sealed file (core rc.9+: only the app’s code reads it; its owner and organization members upload it, and see sealed and the plaintext’s sha256 in listBlobs and blobInfo); appXcalls(app) for a component app’s outbox and dead letters; publishPresence, presence, presenceStream on /apps/{app}/presence.

let png = try Data(contentsOf: imageURL)
let blob = try await client.apps.blobUpload(app: app, body: png, contentType: "image/png")
let content = try await client.blobContent(app, blob.blobId) // content.contentType == "image/png"