> ## Documentation Index
> Fetch the complete documentation index at: https://developers.circle.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Resolve attestation issues

> Troubleshoot and resolve common problems with CCTP attestations

This guide helps you resolve issues when a CCTP attestation takes longer than
expected to become available or when the attestation API returns unexpected
responses.

## Understanding attestation timing

After a successful burn transaction, Circle's Attestation Service (Iris) must:

1. Observe the burn event on the source blockchain
2. Wait for sufficient block confirmations
3. Sign the message and make the attestation available through the
   [CCTP API](/api-reference/cctp/all/get-messages-v2)

This process takes different amounts of time depending on the transfer type:

| Transfer type     | Finality threshold | Typical wait time                                                                                              |
| ----------------- | ------------------ | -------------------------------------------------------------------------------------------------------------- |
| Fast Transfer     | ≤ 1000             | Seconds to a few minutes                                                                                       |
| Standard Transfer | ≥ 2000             | Varies by blockchain (see [Finality and block confirmations](/cctp/concepts/finality-and-block-confirmations)) |

## Why 404 responses are expected

The attestation API returns a 404 response until the attestation service has
observed and processed your burn transaction. This is expected and does not
indicate an error.

The API returns 404 when:

* The burn transaction hasn't reached the required block confirmations
* The attestation service hasn't yet indexed the transaction
* The transaction hash or domain ID is incorrect

Don't treat 404 as a failure. Instead, implement polling with appropriate
intervals.

## Check attestation status

Query the [`GET /v2/messages`](/api-reference/cctp/all/get-messages-v2) endpoint
to check the current status of your attestation.

The following table explains the possible responses and what to do next:

| Response                   | Meaning                             | Action                                                     |
| -------------------------- | ----------------------------------- | ---------------------------------------------------------- |
| 404                        | Attestation not yet observed        | Continue polling until the attestation is available        |
| `{ "messages": [] }`       | Transaction found but not processed | Continue polling until the transaction is processed        |
| `{ "status": "pending" }`  | Awaiting block confirmations        | Continue polling until the block confirmations are reached |
| `{ "status": "complete" }` | Attestation ready                   | Proceed to mint                                            |

## Implement effective polling

Poll the attestation API at regular intervals without exceeding rate limits:

```ts TypeScript theme={null}
async function waitForAttestation(
  sourceDomain: number,
  transactionHash: string,
) {
  const pollInterval = 5000; // Poll every 5 seconds
  const maxWaitTime = 1200000; // 20 minutes maximum
  const startTime = Date.now();

  while (Date.now() - startTime < maxWaitTime) {
    try {
      const response = await fetch(
        `https://iris-api-sandbox.circle.com/v2/messages/${sourceDomain}?transactionHash=${transactionHash}`,
      );

      // 404 is expected while waiting - continue polling
      if (response.status === 404) {
        console.log("Attestation not yet available (404), waiting...");
        await new Promise((resolve) => setTimeout(resolve, pollInterval));
        continue;
      }

      // Rate limited - wait before retrying
      if (response.status === 429) {
        console.log("Rate limited, waiting 5 minutes...");
        await new Promise((resolve) => setTimeout(resolve, 300000));
        continue;
      }

      if (!response.ok) {
        throw new Error(`Unexpected HTTP error: ${response.status}`);
      }

      const data = await response.json();

      // Empty messages array - transaction found but not processed
      if (!data.messages || data.messages.length === 0) {
        console.log("Transaction found, awaiting processing...");
        await new Promise((resolve) => setTimeout(resolve, pollInterval));
        continue;
      }

      const message = data.messages[0];

      // Attestation complete
      if (message.status === "complete" && message.attestation) {
        console.log("Attestation retrieved successfully!");
        return {
          message: message.message,
          attestation: message.attestation,
          decodedMessage: message.decodedMessage,
        };
      }

      // Still pending
      console.log(`Attestation status: ${message.status}`);
      await new Promise((resolve) => setTimeout(resolve, pollInterval));
    } catch (error) {
      console.error("Error fetching attestation:", (error as Error).message);
      await new Promise((resolve) => setTimeout(resolve, pollInterval));
    }
  }

  throw new Error("Attestation not received within maximum wait time");
}
```

### Avoid rate limiting

The attestation service limits requests to 35 per second. If you exceed this
limit, the service blocks all API requests for 5 minutes and returns HTTP 429.

Best practices:

* Use a poll interval of at least 5 seconds
* Implement exponential back-off for repeated 429 responses
* Don't poll from multiple clients for the same transaction

## Troubleshooting checklist

If your attestation isn't available after the expected wait time:

<Steps>
  <Step title="Verify the burn transaction">
    Check the source blockchain's block explorer to confirm:

    * The transaction succeeded (not reverted)
    * The transaction is included in a mined block
    * Sufficient blocks have been confirmed since the transaction
  </Step>

  <Step title="Confirm request parameters">
    Verify you're using the correct:

    * Source domain ID (see
      [Supported Chains and Domains](/cctp/concepts/supported-chains-and-domains))
    * Transaction hash (full hash, including `0x` prefix for EVM chains)
    * API environment (sandbox vs. production)
  </Step>

  <Step title="Check finality requirements">
    Standard Transfers require full finality. For some blockchains, this can take
    significantly longer than Fast Transfers. Check
    [Finality and block confirmations](/cctp/concepts/finality-and-block-confirmations)
    for expected times.
  </Step>

  <Step title="Verify API connectivity">
    Test that you can reach the API:

    ```shell Shell theme={null}
    curl --request GET \
      --url 'https://iris-api-sandbox.circle.com/v2/publicKeys' \
      --header 'Accept: application/json'
    ```

    If this fails, check your network connectivity and firewall settings.
  </Step>
</Steps>
