CERTEN docs
← All docs

Certen Agent Quickstart

Get an autonomous agent authorizing on-chain actions through Certen in a few calls. Certen is a proof‑gated enforcement layer: on‑chain execution — a value transfer or an arbitrary contract call — happens only if a quorum of validators has proven that this exact operation was authorized. Your agent holds its own ed25519 key, signs locally, and the gateway turns those signatures into Accumulate transactions and cryptographic receipts. Certen never holds your keys.

Building governance, voting, a bridge, an agent, or any non‑standard transfer? Start with Proof‑Gated Enforcement — the guarantee (four things that cannot happen), copy‑paste recipes (DAO execution, bridge release‑on‑proof, agent action), and the expectedEvents/expectedState proof‑of‑effect knobs.

0. Get your API key — self-service, ~1 minute

Go to https://gateway.kompendium.co/portal, sign in with Google or email, and click Create key. Your ck_live_… key is shown once — copy it. That's it; no one has to provision anything for you. (You can rotate/revoke keys and invite teammates there too.)

New accounts start on a capped free tier (a few identities, sponsored testnet credits) — plenty to build against; ask us to lift the caps for production volume.

Two keys, don't mix them up

ck_live_… API key your ed25519 key
What authenticates HTTP calls (header X-API-Key) authorizes on-chain actions
Who holds it you create it yourself at /portal you generate and keep it (same key your CARP handshake uses)
Certen sees it (to authenticate you) public key only

You send the API key as X-API-Key: ck_live_… on every request.

Key + signing helper (Node, no deps)

You generate the keypair. Certen only ever receives the public key and your signatures. public_key_hash = sha256(publicKeyBytes). You sign the raw 32-byte hash the gateway hands you.

import crypto from 'crypto';

export function newAgentKey() {
  const { publicKey, privateKey } = crypto.generateKeyPairSync('ed25519');
  const pub = Buffer.from(publicKey.export({ format: 'jwk' }).x, 'base64url'); // 32 bytes
  return {
    publicKey: pub.toString('hex'),                                           // 64 hex
    publicKeyHash: crypto.createHash('sha256').update(pub).digest('hex'),     // 64 hex
    sign: (hashHex) => crypto.sign(null, Buffer.from(hashHex, 'hex'), privateKey).toString('hex'), // 128 hex
  };
}

1. Create the agent's identity (comes pre-funded)

curl -X POST https://gateway.kompendium.co/v1/identity \
  -H "X-API-Key: $CK" -H "Content-Type: application/json" \
  -d '{ "name":"my-agent", "public_key_hash":"<64hex>", "public_key":"<64hex>", "credits":200000 }'

Returns { identity: { id, adi_url, book_url, key_page_url, credit_balance, ... } }. Allow ~90s — it provisions the ADI + key page on-chain and funds credits (sponsored). The page is keyed to your key, so only you can sign for it.

2. The signing pattern (every write uses it)

The gateway never signs. Each write returns a hash_to_sign; you sign it and submit:

  1. Prepare → response includes signing_data.hash_to_sign (and a submit_url).
  2. signature = agentKey.sign(hash_to_sign).
  3. POST the { signature, public_key } to the submit_url.

3. Single-signer governance (e.g. raise threshold)

# prepare
curl -X POST .../v1/governance -H "X-API-Key: $CK" -H "Content-Type: application/json" \
  -d '{ "identity":"acc://my-agent.acme", "operations":[{ "type":"add_key", "public_key_hash":"<ownerKeyHash>" }] }'
# -> { governance_op_id, status:"signing_required", signing_data:{ hash_to_sign, ... }, submit_url }

# sign + submit
curl -X POST .../v1/governance/<governance_op_id>/signature -H "X-API-Key: $CK" -H "Content-Type: application/json" \
  -d '{ "signature":"<128hex>", "public_key":"<64hex>" }'

Operations: add_key / remove_key (public_key_hash), set_threshold (threshold), add_delegate / remove_delegate (delegate_url), add_authority / remove_authority (authority_url).

4. Multi-sig (the authorization scheme)

Make a key page M-of-N, then actions require M signatures.

Set it up (single-sig while threshold is still 1): add_key the second signer, then set_threshold to 2. The page is now 2-of-2.

Authorize an action (2 signers):

  1. First signer calls /v1/governance and submits a signature (step 3). Response is now honest: status:"pending_signatures", collected_signatures:1, required_signatures:2, is_ready:false. Keep the signing_data.transaction_hash — that's the pending tx.
  2. Second signer signs the same pending tx by hash:
    curl -X POST .../v1/sign -H "X-API-Key: $CK" -H "Content-Type: application/json" \
      -d '{ "type":"pending_tx", "target_id":"<transaction_hash>", "identity":"acc://my-agent.acme",
            "signer_url":"acc://my-agent.acme/book/1", "public_key":"<signer2 64hex>" }'
    # -> signing_data.data_for_signature  +  submit_url
    curl -X POST .../v1/sign/<sign_request_id>/signature -H "X-API-Key: $CK" -H "Content-Type: application/json" \
      -d '{ "signature":"<128hex>", "public_key":"<signer2 64hex>" }'
    
    When the threshold is met, Accumulate executes the transaction automatically.

Watch progress any time:

curl ".../v1/pending?identity=acc://my-agent.acme" -H "X-API-Key: $CK"

Each item reports collected_signatures, total_authorities (threshold), user_has_signed, awaiting_authorities, and is_ready — read live from chain.

Delegation note: adding another agent's key book as an authority/delegate is not unilateral — the added authority must also sign. Until it does, the action stays pending and its book appears in awaiting_authorities. The delegate consents using the same /v1/sign pending_tx call with its identity.

5. Get settlement evidence

For any executed transaction, fetch the Accumulate-native merkle receipt (inclusion proof → anchor):

curl .../v1/proof/tx/<transaction_hash>/receipt -H "X-API-Key: $CK"
# -> { status:"delivered", anchored:true, receipt:{ start, anchor, entries } }

6. Cross-chain execution — native transfers AND arbitrary contract calls (proof-gated)

POST /v1/transaction runs a cross-chain leg on the target chain, executed only if the multi-validator proof authorizes that exact operation. Same 2-step signing as above (hash_to_sign → sign → submit_url). A leg is either a native transfer or an arbitrary contract call — identical proof rigor for both.

Arbitrary contract call (the validators ABI-encode functionSignature+args and execute target.call{value}(data), gated to that exact (target, value, calldata)):

# prepare
curl .../v1/transaction -H "X-API-Key: $CK" -d '{
  "identity_id": "<your identity id>",
  "intent": {
    "adiUrl": "acc://your-org.acme",
    "legs": [{
      "legId": "leg-1", "chain": "ethereum-sepolia", "chainId": 11155111,
      "fromAddress": "<your abstract account>", "toAddress": "<contract>", "amount": "0",
      "contractCall": {
        "target": "<contract>", "value": "0",
        "functionSignature": "buy(bytes32)", "args": ["0x<orderId>"],
        "expectedEvents": [{ "contract": "<contract>",
          "topic0": "<keccak256(\"Paid(bytes32,address)\")>" }]
      }
    }]
  }
}'
# -> { intent_id, status:"signing_required", signing_data:{ hash_to_sign }, submit_url }
# sign hash_to_sign, then POST {signature, public_key} to submit_url

Flow at a glance

generate ed25519 key ──▶ POST /v1/identity (funded) ──▶ sign hashes locally
        │                                                      │
        └── multisig: add_key → set_threshold ── sign 1 (pending) → sign 2 (executes)
                                                                       │
                                                  GET /v1/pending (progress) · /v1/proof/tx/:hash/receipt (evidence)

Full endpoint reference: https://gateway.kompendium.co/reference.