---
title: "Issue an EIP-712 signed risk attestation for on-chain use"
method: POST
path: "/v1/attest"
tags: ["Attestation"]
---

# Issue an EIP-712 signed risk attestation for on-chain use

`POST /v1/attest`

## Request

- Base URL: `https://intelapi.publicaml.org`
- URL: `https://intelapi.publicaml.org/v1/attest`
- Auth: one of:
  - API key in header `X-API-Key`
  - none

Signs a four-field struct a smart contract can verify, so a protocol can gate on our risk reading without trusting an HTTP response. Valid for 600 seconds; there is no nonce and no revocation channel, so that window is the only bound on acting on a stale verdict.

`attestation` is exactly what the contract expects. The risk detail beside it is UNSIGNED context for humans and off-chain callers — it is not part of what the signature covers.

bitcoin cannot be attested at all: the struct types the subject as a Solidity address, which cannot hold a base58 or bech32 address. This endpoint also accepts the testnets SEPOLIA and NILE.

The signed payload format is FROZEN — an extra field would change the hash and break every deployed verifier.

## Request body

- object
  - `wallet_address` string, required: The address to look up, in its normal display form. * bitcoin — base58 (`1…`, `3…`) or bech32/bech32m (`bc1…`, `bc1p…`). Case is SIGNIFICANT for base58; do not lowercase it. A 64-character lowercase TRANSACTION HASH is also accepted here: on bitcoin the graph is bipartite (address → transaction → address), so a transaction is a node with its own neighbours and its own findings. CoinJoin is classified on the transaction, not on the participants, and this is how you read it. * ethereum, bsc — `0x` + 40 hex characters, any case. * tron — base58 (`T…`, 34 characters); the `0x` 20-byte form is accepted too and is what the API stores and echoes.
  - `chain` 'ethereum' | 'bsc' | 'tron' | 'SEPOLIA' | 'NILE', required: bitcoin is not attestable — see the description. SEPOLIA and NILE are testnets.
  - `include` string[]: Echo the full EIP-712 payload, as a debugging aid.

## Response `200`

The signed attestation.

- object
  - `status` string
  - `message` string
  - `result` object
    - `attestation` object: The signed struct, exactly as the contract reads it.
    - `signature` string
    - `signer` string
    - `domain` object
    - `risk_score` number, nullable: Unsigned context.

## Other responses

- `400`: Malformed request.
- `429`: Rate limited: anonymous callers, and keys issued with a quota of their own. Authenticate to remove the limit.
- `500`: Server error.
- `503`: Refusing to sign: the signer is unconfigured, or a fact source is unavailable. Retry rather than caching a verdict.

---

[API](https://skmtc.dev/publicaml/apis/publicaml-intelligence-api.md) · [All operations](https://skmtc.dev/publicaml/apis/publicaml-intelligence-api/llms.txt) · [OpenAPI document](https://skmtc.dev/publicaml/apis/publicaml-intelligence-api/revisions/7c0c6315088c?raw)
