CERTEN docs
← All docs

Getting Started with the CERTEN Gateway API

This guide walks through the core workflows of the CERTEN Gateway API using curl examples.

Base URL

https://gateway.kompendium.co

Confirm it before anything else — one request, no key needed:

curl https://gateway.kompendium.co/v1/health     # {"status":"healthy"}

The live OpenAPI spec is public at /docs (JSON at /docs/json). It is generated from the running server, so it is authoritative for request and response shapes — including every endpoint this guide does not cover. When this page and the spec disagree, the spec is right.

The SDK and CLI default to the same host and both honor a CERTEN_API_URL override, so a staging or self-hosted gateway needs no code change:

export CERTEN_API_URL="https://gateway.staging.internal"

1. Get an API Key

Sign in at https://gateway.kompendium.co/portal and mint one. It is self-service — Google or email sign-in, no ticket, no waiting on anyone. The portal is also where you rotate keys, revoke them, and invite teammates.

The key is shown once, in the format ck_live_.... It cannot be retrieved again; mint a new one if you lose it.

export API_KEY="ck_live_your_key_here"

Prefer the CLI? It stores the key in your OS keyring rather than a shell history file:

npm install -g @certen.io/cli
certen auth login --api-key "$API_KEY"

New organizations start with sandbox limits (a capped credit grant and a small number of identities), which is enough to run everything in this guide. Ask an operator to approve the org when you need more.

2. Generate a signing key

An identity is controlled by an Ed25519 key that you hold. CERTEN never has it, which is why nobody here can move your assets — and also why you have to make one before there is anything to create.

certen keys generate --name dev

That prints a public_key and a public_key_hash, encrypts the private key with your passphrase, and writes it to ~/.certen/keys/dev.json (mode 0600).

If you are producing the key yourself rather than with the CLI: public_key_hash is sha256(raw 32-byte Ed25519 public key) — the hash of the key BYTES, not of its hex spelling and not of the SPKI DER. Getting this wrong creates an identity bound to a key you do not have, and the failure does not surface until the first signature is rejected.

# The equivalent in Node, if you are not using the CLI
node -e "const c=require('crypto');const{publicKey}=c.generateKeyPairSync('ed25519');
const spki=publicKey.export({format:'der',type:'spki'});const raw=spki.subarray(spki.length-32);
console.log('public_key     ',raw.toString('hex'));
console.log('public_key_hash',c.createHash('sha256').update(raw).digest('hex'));"

3. Create an Identity

An identity is an Accumulate ADI with associated key book, key page, and optional chain accounts.

certen identity create --name my-company --sign-with dev \
  --chains ethereum-sepolia,polygon-amoy

Or over HTTP, supplying the key material from the previous step:

curl -X POST https://gateway.kompendium.co/v1/identity \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "name": "my-company",
    "public_key_hash": "c2446def78b3ec4de53ee1939cf1b5e2bfaf8bd8c80a2821b4b05bea1ad44b83",
    "public_key": "e6f9930f50298ba7d1cad45e902cb8eaa5ba6b3752a6666a1ca0c128721d6a0b",
    "chains": ["ethereum-sepolia", "polygon-amoy"],
    "credits": 50000
  }'

Send an Idempotency-Key on every POST. A network error is otherwise indistinguishable from success, and a blind retry can create a second identity against your quota.

Response — 202 Accepted, not 200. Provisioning writes an ADI, key book, and key page to Accumulate, which takes tens of seconds, so the status is creating and chain_accounts is still empty:

{
  "identity": {
    "id": "0fbdbe8c-4895-4a9e-88e4-ae8f852c67a9",
    "adi_url": "acc://my-company.acme",
    "book_url": "acc://my-company.acme/book",
    "key_page_url": "acc://my-company.acme/book/1",
    "status": "creating",
    "chain_accounts": [],
    "credit_balance": 0,
    "created_at": "2026-07-31T20:33:08.922Z"
  },
  "status_url": "/v1/identity/0fbdbe8c-4895-4a9e-88e4-ae8f852c67a9"
}

Poll until it is usable

certen identity get 0fbdbe8c-4895-4a9e-88e4-ae8f852c67a9

Wait for status: "active". Typically 20–60 seconds. If status reaches failed, error_message says why.

Do not treat can_sign as readiness. It reports whether the gateway holds key material for this identity — for an external-signing identity, simply whether a public_key was supplied. It is true from the moment the identity row is created, including while the ADI does not yet exist on chain and including when provisioning has stalled. It answers "is a key on file", not "can this identity sign".

status is the field that tells you whether provisioning finished. If you want proof the identity is genuinely usable, query the key page directly:

curl -s -X POST https://kermit.accumulatenetwork.io/v3   -H 'Content-Type: application/json'   -d '{"jsonrpc":"2.0","id":1,"method":"query","params":{"scope":"acc://my-company.acme/book/1"}}'

That returns the on-chain key page and the key hashes on it — the only answer that cannot be wrong.

4. Check Your Portfolio

View all identities and their multi-chain balances at a glance.

curl https://gateway.kompendium.co/v1/portfolio \
  -H "X-API-Key: $API_KEY"

5. Execute a Transaction

Transactions follow a prepare-sign-submit lifecycle. The CLI does all three in one command:

certen tx create --identity <uuid> --to-chain ethereum-sepolia \
  --to 0xRecipient --amount 1000000000000000 --sign-with dev

The three steps below are the same flow over HTTP, for when the signer is an HSM, an air-gapped machine, or your own policy engine.

Step 1: Prepare

curl -X POST https://gateway.kompendium.co/v1/transaction \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "identity_id": "your-identity-uuid",
    "intent": {
      "type": "sendTokens",
      "to": "acc://recipient.acme/tokens",
      "amount": 100
    },
    "proof_class": "standard"
  }'

Response:

{
  "intent_id": "uuid",
  "status": "signing_required",
  "signing_data": {
    "request_id": "uuid",
    "transaction_hash": "abcdef...",
    "hash_to_sign": "123456..."
  }
}

Step 2: Sign hash_to_sign

certen keys sign --name dev --hash 123456...
# Output: signature (128 hex)

This sends nothing anywhere — it prints a signature. It is also the air-gapped path: carry the hash to the machine holding the key, carry the signature back.

Signing without the CLI? Sign the raw BYTES of hash_to_sign. Do not hash it again, and do not sign the ASCII of the hex string. All three mistakes produce a well-formed 128-hex signature that the gateway rejects with an error naming none of them.

Note the field name: a new intent returns the bytes as signing_data.hash_to_sign, while co-signing something that already exists (POST /v1/sign) returns them as signing_data.data_for_signature. Same contract, different name.

Step 3: Submit the Signature

curl -X POST https://gateway.kompendium.co/v1/transaction/{intent_id}/signature \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "signature": "signature_hex_from_step_2",
    "public_key": "ed25519_public_key_hex"
  }'

Response:

{
  "intent_id": "uuid",
  "status": "submitted",
  "tx_hash": "abcdef..."
}

6. Check Transaction Status / Get Proof

# Get transaction details (includes proof when available)
curl https://gateway.kompendium.co/v1/transaction/{intent_id} \
  -H "X-API-Key: $API_KEY"

# Get proof by ID
curl https://gateway.kompendium.co/v1/proof/{proof_id} \
  -H "X-API-Key: $API_KEY"

# Get proof by transaction hash
curl https://gateway.kompendium.co/v1/proof/tx/{tx_hash} \
  -H "X-API-Key: $API_KEY"

7. List Pending Actions

View pending multi-sig approvals and governance actions awaiting your signature.

# List all pending actions
curl "https://gateway.kompendium.co/v1/pending" \
  -H "X-API-Key: $API_KEY"

# Filter by identity and category
curl "https://gateway.kompendium.co/v1/pending?identity=acc://my-company.acme&category=governance&limit=20" \
  -H "X-API-Key: $API_KEY"

Response:

{
  "actions": [
    {
      "id": "uuid",
      "identity_url": "acc://my-company.acme",
      "category": "governance",
      "type": "updateKeyPage",
      "status": "eligible",
      "tx_hash": "abcdef...",
      "collected_signatures": 1,
      "total_authorities": 2,
      "user_has_signed": false,
      "discovered_at": "2025-01-01T00:00:00Z"
    }
  ],
  "stats": { "total": 5, "urgent": 1, "governance": 2, "transactions": 3, "awaiting_others": 1 },
  "pagination": { "limit": 100, "offset": 0, "total": 5 }
}

8. Sign a Pending Action

Signing a pending action is a two-step process: prepare the signing data, then submit the signature.

Step 1: Create a Sign Request

curl -X POST https://gateway.kompendium.co/v1/sign \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "pending_action",
    "target_id": "pending-action-uuid",
    "vote": "approve"
  }'

Response:

{
  "sign_request_id": "uuid",
  "status": "signing_required",
  "signing_data": {
    "data_for_signature": "hex...",
    "transaction_hash": "hex...",
    "signer_url": "acc://my-company.acme/book/1",
    "signer_version": 1,
    "timestamp": 1234567890
  },
  "submit_url": "/v1/sign/{sign_request_id}/signature"
}

Step 2: Submit the Signature

curl -X POST https://gateway.kompendium.co/v1/sign/{sign_request_id}/signature \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "signature": "signature_hex",
    "public_key": "public_key_hex"
  }'

9. Governance Operations

Modify key pages (add/remove keys, delegates, thresholds) or account authorities.

Prepare a Keypage Update

curl -X POST https://gateway.kompendium.co/v1/governance \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "identity": "acc://my-company.acme",
    "operations": [
      { "type": "add_delegate", "delegate_url": "acc://partner.acme/book" },
      { "type": "set_threshold", "threshold": 2 }
    ]
  }'

Response:

{
  "governance_op_id": "uuid",
  "status": "signing_required",
  "signing_data": {
    "request_id": "uuid",
    "transaction_hash": "hex...",
    "hash_to_sign": "hex...",
    "key_page_version": 1
  },
  "submit_url": "/v1/governance/{governance_op_id}/signature"
}

Submit the Governance Signature

curl -X POST https://gateway.kompendium.co/v1/governance/{governance_op_id}/signature \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "signature": "signature_hex",
    "public_key": "public_key_hex"
  }'

Check Governance Operation Status

curl https://gateway.kompendium.co/v1/governance/{governance_op_id} \
  -H "X-API-Key: $API_KEY"

Supported Governance Operations

Type Description Required Field
add_delegate Add a delegate authority delegate_url
remove_delegate Remove a delegate authority delegate_url
set_threshold Set signature threshold threshold
add_key Add a key to key page public_key_hash
remove_key Remove a key from key page public_key_hash
add_authority Add an authority to an account authority_url
remove_authority Remove an authority from an account authority_url

Note: Keypage operations (add_delegate, remove_delegate, set_threshold, add_key, remove_key) and authority operations (add_authority, remove_authority) cannot be mixed in a single request.