ITEN
Demo · Base Sepolia · test money

Scontracts API

The API is JSON over HTTP, at https://scontratto-api.scontratto.workers.dev. It is the same API used by agents: one Worker, one database, different routes.

Amounts travel as decimal strings in EURC's smallest units, with 6 decimals. Deadlines are integers in Unix seconds.

Reads are open. In the pilot, writes require the pilot token:

Authorization: Bearer <pilot token>

Reads

GET /health

{ ok, chainId, escrow, attestor, acp, mandateHook }: the chain, the addresses of the escrow and of its attestor, and those of the agent contracts.

GET /contratti

The latest 100 Scontracts, most recent first: { contratti: [{ id, stato, tracking, scadenza }] }.

GET /contratti/:id

The full Scontract: terms, status, condition hash, nonce, payee signature, attestation with the oracle's proof, settlement transaction reference, and the event history.

Writes

POST /contratti/prepara

The mandate for the payee to sign.

{
  "terms": {
    "payer": { "id": "payer", "proof": "wallet", "address": "0x...", "display": "Buyer" },
    "payee": { "id": "payee", "proof": "wallet", "address": "0x...", "display": "Seller" },
    "money": { "amount": "5000000", "token": "EURC", "address": "0x808456652fdb597867f38412077A9182bf77359F" },
    "condition": { "kind": "parcel-delivered", "carrier": "dhl", "trackingNumber": "1234567890" },
    "deadline": 1792000000,
    "onExpiry": "refund"
  }
}

Returns { id, nonce, mandate, domain }. mandate is the EIP-712 document to sign, nonce must be kept for the next step, and domain gives the escrow's address.

The payee signs it with eth_signTypedData_v4, in the domain { name: "Scontratto", version: "1", chainId: 84532, verifyingContract: <escrow> }, with the type:

Mandate(address payer, address payee, address token, uint256 amount,
        bytes32 conditionHash, uint64 deadline, bool executeOnExpiry, bytes32 nonce)

POST /contratti

Creates the Scontract with the payee's signature: { terms, nonce, payeeSignature }. Returns 201 with status draft. Rejects with 400 if the signature isn't the payee's, or isn't on these terms.

Then the payer acts on-chain, from their wallet:

  1. approve(escrow, amount) on the EURC token;
  2. Escrow.open(mandate, payeeSignature, attestor), where attestor is the address returned by /health;
  3. Escrow.fund(id).

POST /contratti/:id/finanziato

After funding: the API checks on-chain that the Scontract really is funded and records it. Rejects with 409 if it isn't.

POST /contratti/:id/osserva

Observes the condition now, without waiting for the cron. If the condition is met, the attestor signs and the Scontract executes. If the Scontract has expired, it closes. You can call it as many times as you like.

POST /webhooks/aftership

The AfterShip webhook. It is accepted only with the HMAC signature of the body in the aftership-hmac-sha256 header, and the webhook token in x-scontratto-token. It immediately observes the funded Scontracts with that tracking number.

Errors

Every error is { "errore": "..." }. Errors are in Italian by default. Send Accept-Language: en, or add ?lang=en, to get them in English. HTTP statuses and codes never change.

Status When
400 Malformed terms, for example an invalid address or a non-integer amount, or a signature that isn't the payee's.
401 The pilot token is missing or wrong.
404 The Scontract doesn't exist.
409 The Scontract isn't funded on-chain.

The cron

Every five minutes, the Worker observes all funded Scontracts and closes the expired ones. A Scontract that fails, for example because the courier doesn't respond, doesn't stop the others: it is retried on the next cycle.