Committing approval rules
Commit a change to the Org's approval rules with passkeys from two or more people.
The base URL is https://api.clavis.nexus and the path is /orgs/{org}/approval-requests. Authorisation is the session JWT plus a SignProof in the body (see Authentication).
Steps
1. Open the organisation (Org) and set two or more committers at
/developers/{orgId}/settings/governance.2. An owner or admin opens a request with
POST …/approval-requests.RequestPOST https://api.clavis.nexus/orgs/<org>/approval-requests Authorization: Bearer <session JWT> Content-Type: application/json { "request_kind": "policy_revision", "policy_revision_hash": "<64 hex>", "target_domain": "<domain>", "sign_proof": { "v": 1, "purpose": "org.approval.request", "intent_type": "generic_sign", "intent_id": "<intent_id>", "rp_id": "<rp id>", "origin": "https://<host>", "counter_previous": 0, "public_key_cose_b64u": "<base64url>", "message_digest": "<base64url>", "sub": "<passkey sub>", "challenge": "<base64url>", "binding_nonce": "<base64url>", "credential_id": "<base64url>", "authenticator_data": "<base64url>", "client_data_json": "<base64url>", "signature": "<base64url>", "signed_at": 1710000000 } }policy_revision_hashFrom the response of POST /orgs/<org>/policy-revisions. The current rules return POLICY_UNCHANGED; an unknown hash returns UNKNOWN_POLICY.target_domainTrim it, lowercase it and drop any trailing dot. Then take SHA-256 of the digest input as UTF-8 and encode it as Base64URL. Do not add whitespace.request_idUsed for the votes and the commit that follow.sign_proofWith purpose org.approval.request, obtained after a passkey signature in the developer console.
Response
{ "request_id": "<id>", "expires_at": 1710000900 }Digest input
{"created_by_sub":"<session sub>","org_id":"<org UUID>","policy_revision_hash":"<64 hex>","request_kind":"policy_revision","target_domain":"<domain>"}3. Each voter needed for the commit votes with
POST …/votes. The body is asign_proofwith purposeorg.policy.vote.RequestPOST https://api.clavis.nexus/orgs/<org>/approval-requests/<request_id>/votes Authorization: Bearer <session JWT> Content-Type: application/json { "sign_proof": { "v": 1, "purpose": "org.policy.vote", "intent_type": "generic_sign", "intent_id": "<intent_id>", "rp_id": "<rp id>", "origin": "https://<host>", "counter_previous": 0, "public_key_cose_b64u": "<base64url>", "message_digest": "<base64url>", "sub": "<passkey sub>", "challenge": "<base64url>", "binding_nonce": "<base64url>", "credential_id": "<base64url>", "authenticator_data": "<base64url>", "client_data_json": "<base64url>", "signature": "<base64url>", "signed_at": 1710000000 } }Response
{ "ok": true, "state": "<state>" }Digest input
{"approval_request_id":"<request_id>","policy_revision_hash":"<64 hex>","voter_sub":"<session sub>"}4. Once the threshold is reached, commit with
POST …/commit. The body is asign_proofwith purposeorg.approval.commit.RequestPOST https://api.clavis.nexus/orgs/<org>/approval-requests/<request_id>/commit Authorization: Bearer <session JWT> Content-Type: application/json { "sign_proof": { "v": 1, "purpose": "org.approval.commit", "intent_type": "generic_sign", "intent_id": "<intent_id>", "rp_id": "<rp id>", "origin": "https://<host>", "counter_previous": 0, "public_key_cose_b64u": "<base64url>", "message_digest": "<base64url>", "sub": "<passkey sub>", "challenge": "<base64url>", "binding_nonce": "<base64url>", "credential_id": "<base64url>", "authenticator_data": "<base64url>", "client_data_json": "<base64url>", "signature": "<base64url>", "signed_at": 1710000000 } }Response
{ "ok": true, "org_status": "<status>", "approval_manifest": null }Digest input
{"committer_sub":"<session sub>","org_id":"<org UUID>","request_id":"<request_id>"}
Endpoints
The HTTP for approval-requests, and reading a request.
POST …/approval-requestsOpens a request. Needs Authorization and a sign_proof.GET …/approval-requests/{id}Reads the vote totals and ready_for_finalize.POST …/votesAdds one vote.POST …/commitCommits the change.
GET https://api.clavis.nexus/orgs/<org>/approval-requests/<request_id>
Authorization: Bearer <session JWT>Response
{
"request": {},
"vote_count": 1,
"weight_sum": 1,
"ready_for_finalize": false
}Scope
- Use these steps when the Org has two or more committers.
- Votes are cast in the developer console.
Reference
- Authentication — The session JWT and SignProof purposes.
- Quickstart — Setting up the Org, the app and the host check.