> ## Documentation Index
> Fetch the complete documentation index at: https://pulkit-fix-docs.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Testnet API

> Query testnet attestations, schemas and identities over HTTP. The same API the Explorer uses.

The Attestations API indexes the registry contracts and serves the result as JSON. The Explorer is built on it, so anything you can see there you can fetch here.

```text theme={null}
https://attestations-api-testnet.billions.network/api/v1
```

The examples below set it once:

```bash theme={null}
export API=https://attestations-api-testnet.billions.network/api/v1
```

<Warning>
  This is the testnet API, for development and experiments. For checking whether an agent has a verified human in a real application, use the production endpoints described in [Verify an Agent](/agents/verify-human-agent-pairing#querying-the-attestations-api-directly).
</Warning>

## Pagination

List endpoints take `page_number` and `page_size` and wrap results in the same envelope:

```json theme={null}
{
  "totalItems": 47,
  "uniqueItems": 17,
  "totalPages": 47,
  "pageNumber": 1,
  "pageSize": 1,
  "nextPage": 2,
  "data": [ ... ]
}
```

`nextPage` is `null` on the last page. `uniqueItems` on the attestations list is the number of distinct attesters.

## Health check

```bash theme={null}
curl "$API/healthCheck"
```

Returns `Ok` when the service is up.

## Attestations

### List and filter

```bash theme={null}
curl "$API/attestations?page_number=1&page_size=10"
```

Add any of these query parameters to narrow the list. They combine.

| Parameter | Filters by |
| - | - |
| `schemaId` | Schema the attestation was written against |
| `attesterDid`, `attesterIden3Id`, `attesterEthereumAddress` | Who made it |
| `recipientDid`, `recipientIden3Id`, `recipientEthereumAddress` | Who it is about |

For example, every review of one agent:

```bash theme={null}
curl "$API/attestations?schemaId=0x3ff27d25d9a49e12c43465525fb4da6fa572edc013d23caa1ce96f3d8b649442&recipientDid=<agent-did>"
```

Each item in `data` looks like this (trimmed):

```json theme={null}
{
  "id": "0x3c84a465a9365b819f8c9482773c49739a468cbcd335481322b1d6cdb20ba673",
  "creationTime": 1776776463,
  "schemaInfo": {
    "id": "0x3ff27d25d9a49e12c43465525fb4da6fa572edc013d23caa1ce96f3d8b649442",
    "name": "Review",
    "schemaSignatures": [
      { "name": "stars", "type": "uint8" },
      { "name": "comment", "type": "string" }
    ]
  },
  "fromDid": "did:iden3:billions:test:2W555P5b6B8G8FPnYTkvn34Wn2isLvibjuaV4mRDa2",
  "fromId": "20720290774732886907568363010712047654987517739085103469744356447088194049",
  "fromEthereumAddress": "0xcabaE40A68BCB419c8d9A3ab5EAEf63810c2d4f4",
  "toName": "Test Agent 6",
  "toDid": "did:iden3:billions:main:2VmAkXrihYaL5GsMCfGE5ae3f5QDz6XpAhuU9STdaa",
  "toId": "19801292195914168711179660065648943934654113027992910040720806296179224833",
  "toEthereumAddress": "0xcabaE40A68BCB419c8d9A3ab5EAEf63810c2d4f4",
  "decodedDataJson": "[{\"name\":\"stars\",...,\"value\":{\"name\":\"stars\",\"type\":\"uint8\",\"value\":\"4\"}}, ...]"
}
```

`creationTime` is a Unix timestamp in seconds. `decodedDataJson` is a **string** containing JSON, so parse it a second time.

### Read the decoded data

Each entry in `decodedDataJson` nests the actual value under `value.value`:

```javascript theme={null}
const fields = JSON.parse(attestation.decodedDataJson);
const stars = fields.find((f) => f.value.name === "stars").value.value; // "4"
const comment = fields.find((f) => f.value.name === "comment").value.value;
```

Values come back as strings, including numbers.

### Get one attestation

```bash theme={null}
curl "$API/attestations/<attestation-id>"
```

The detail response has everything in the list item plus these fields:

| Field | Meaning |
| - | - |
| `txid` | Hash of the transaction that recorded it |
| `expirationTime` | Unix time it expires, or `0` for never |
| `revocable` | Whether it can be revoked |
| `revoked` | Whether it has been |
| `revocationTime` | When it was revoked, or `0` |
| `rawData` | The ABI-encoded data before decoding |

## Schemas

### List schemas

```bash theme={null}
curl "$API/schemas?page_size=10"
```

```json theme={null}
{
  "index": "3",
  "id": "0x3ff27d25d9a49e12c43465525fb4da6fa572edc013d23caa1ce96f3d8b649442",
  "name": "Review",
  "resolver": "0x0000000000000000000000000000000000000000",
  "attestationsCount": 6,
  "schemaSignatures": [
    { "name": "stars", "type": "uint8" },
    { "name": "comment", "type": "string" }
  ]
}
```

### Get one schema

```bash theme={null}
curl "$API/schemas/0x3ff27d25d9a49e12c43465525fb4da6fa572edc013d23caa1ce96f3d8b649442"
```

This adds the schema's creator (`creator`, `creatorDid`, `creatorId`), the creation transaction (`txid`), `revocable`, the raw string (`schemaRaw`, for example `uint8 stars,string comment`), and a page of attestations written against it.

## Search

One endpoint that works out what you gave it:

```bash theme={null}
curl "$API/search/<schema-id | attestation-id | did | name | 0x-address>"
```

The response includes a `type` field telling you what matched. For example, searching for a schema ID returns:

```json theme={null}
{
  "type": "schema",
  "id": "0xca354bee6dc5eded165461d15ccb13aceb6f77ebbb1fd3fe45aca686097f2911",
  "schemaInfo": { "name": "AgentOwnership", "schemaSignatures": [ ... ] }
}
```

A DID returns `attesterDid` or `recipientDid`, depending on which side it appears on.

## Everything about one identity

```bash theme={null}
curl "$API/identities/<did | 0x-address | iden3-id>/attestations"
```

Returns every attestation where that identity is the attester or the recipient, paginated. The identifier can be a DID, an Ethereum address, or a raw iden3 ID, and the API tells them apart by format. This is the call behind the Explorer's agent and human lookups.

## Nullifier

```bash theme={null}
curl "$API/nullifier?userId=<iden3-user-id>"
```

Returns `{ "userId": "...", "nullifier": "..." }`, both as strings. The nullifier is a stable, privacy-preserving identifier for the person behind a verified identity. It is useful for deduplication, such as stopping one person from claiming something through two agents. `userId` is required and must be an integer, otherwise the API responds with `400`.

## Worked example: average rating for an agent

This is the logic from the `identity:reviews` script in the [examples repo](https://github.com/BillionsNetwork/attestations-examples), written as a standalone Node script.

```javascript theme={null}
const API = "https://attestations-api-testnet.billions.network/api/v1";
const REVIEW_SCHEMA = "0x3ff27d25d9a49e12c43465525fb4da6fa572edc013d23caa1ce96f3d8b649442";

async function getReviews(agentDid) {
  const reviews = [];
  let page = 1;

  while (page) {
    const url = `${API}/attestations?schemaId=${REVIEW_SCHEMA}&recipientDid=${agentDid}&page_number=${page}&page_size=20`;
    const body = await fetch(url).then((r) => r.json());
    reviews.push(...body.data);
    page = body.nextPage;
  }
  return reviews;
}

const reviews = await getReviews(process.argv[2]);
const stars = reviews.map((r) => {
  const fields = JSON.parse(r.decodedDataJson);
  return Number(fields.find((f) => f.value.name === "stars").value.value);
});

const average = stars.length ? stars.reduce((a, b) => a + b, 0) / stars.length : 0;
console.log(`${average.toFixed(2)} stars from ${stars.length} reviews`);
```

Run it with an agent DID that has reviews:

```bash theme={null}
node reviews.mjs did:iden3:billions:main:2VmAkXrihYaL5GsMCfGE5ae3f5QDz6XpAhuU9STdaa
```

## Next

<Card title="Testnet Explorer" icon="flask" href="/agents/attestation-testnet-explorer">
  Create the records you're querying here.
</Card>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.