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

# AI and cigar matching

> Detect, analyze, and identify cigars from images with Boxpressd AI endpoints.

Boxpressd AI endpoints turn cigar and band images into catalog-backed matches. You can call the complete workflow or compose individual stages when you need more control.

All routes require `x-boxpressd-key` and the `api.ai` entitlement. Send image URLs that the Boxpressd service can retrieve. A request can include one `imageUrl` or up to eight `imageUrls`, unless the endpoint says otherwise.

## Choose a workflow

Use `POST /v1/matches` or `POST /v1/ai/cigars/identify` for most applications. The two routes share the same request and response contract.

Use the staged endpoints when you need to inspect evidence, tune a scan flow, or reuse prior results.

| Endpoint | Purpose | Paid AI gate |
| - | - | - |
| `POST /v1/ai/cigars/detect` | Detect and crop cigars in a larger image. | No paid inference |
| `POST /v1/ai/cigars/preprocess` | Crop and normalize an image for recognition. | No paid inference |
| `POST /v1/ai/cigars/similar` | Find visually similar catalog cigars. | No paid inference |
| `POST /v1/ai/cigars/text/recognize` | Read visible band and barcode text. | Credit-gated |
| `POST /v1/ai/cigars/fingerprint` | Extract a structured band appearance fingerprint. | Credit-gated |
| `POST /v1/ai/cigars/fingerprint/recognize` | Match a fingerprint against stored catalog evidence. | No paid inference |
| `POST /v1/ai/cigars/wrapper/analyze` | Compare wrapper evidence among plausible variants. | Credit-gated |
| `POST /v1/ai/cigars/band/analyze` | Compare band-color evidence among plausible candidates. | Credit-gated |
| `POST /v1/ai/cigars/identify` | Run the complete identification and research workflow. | Credit-gated |

<Info>
  “No paid inference” means the route does not spend paid model usage. These routes still require AI access to be enabled for the application.
</Info>

## Identify a cigar from a band image

Use a unique idempotency key for each logical paid operation. You can safely retry a request after a connection failure with the same key. Reusing a completed key returns `409 ai_request_replayed` instead of running and charging the operation again.

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://api.boxpressd.com/v1/matches" \
    --request POST \
    --header "x-boxpressd-key: $BOXPRESSD_DEVELOPER_API_KEY" \
    --header "Idempotency-Key: band-scan-7f52b86d" \
    --header "Content-Type: application/json" \
    --data "{\"imageUrl\":\"$CIGAR_BAND_IMAGE_URL\",\"researchMode\":\"auto\",\"suggestionLimit\":5}"
  ```

  ```javascript Fetch theme={null}
  const response = await fetch("https://api.boxpressd.com/v1/matches", {
    method: "POST",
    headers: {
      "x-boxpressd-key": process.env.BOXPRESSD_DEVELOPER_API_KEY,
      "Idempotency-Key": crypto.randomUUID(),
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      imageUrl: process.env.CIGAR_BAND_IMAGE_URL,
      researchMode: "auto",
      suggestionLimit: 5,
    }),
  });

  const result = await response.json();
  const remainingCredits = response.headers.get(
    "x-boxpressd-ai-credits-remaining",
  );
  ```
</CodeGroup>

The response uses one of three statuses:

| Status | Meaning |
| - | - |
| `matched` | The workflow found a catalog-backed winner above the decision threshold. |
| `suggestions` | The evidence supports candidates but not an automatic match. Ask the user to confirm. |
| `unmatched` | The workflow could not support a catalog match. |

`match` and `suggestions` contain database-backed cigar IDs. `pendingSuggestions` can contain sourced research candidates that are not yet linked to a Boxpressd catalog record. Do not treat confidence values as calibrated probabilities.

## Compose a staged match

A staged client commonly runs:

1. `similar`, `text/recognize`, and `fingerprint` with the same image.
2. `fingerprint/recognize` with the complete fingerprint response.
3. `band/analyze` when two or more plausible candidates remain.
4. `identify` with the complete earlier responses under `stageResults`.

Identify combines supplied stage evidence without repeating the earlier embedding work. A clear, corroborated catalog winner can return immediately. Inconclusive evidence can continue to bounded research.

```json theme={null}
{
  "imageUrl": "https://your-cdn.example/cigars/scan-1842.jpg",
  "researchMode": "auto",
  "researchProfile": "quick",
  "stageResults": {
    "similar": { "candidates": [] },
    "text": { "candidates": [] },
    "fingerprint": { "schemaVersion": "band-appearance-v2" },
    "recognize": { "candidates": [] },
    "bandAnalysis": { "analyzed": false }
  }
}
```

Pass complete endpoint responses in `stageResults`; do not reduce them to IDs and scores. The service uses diagnostic fields when it reconciles evidence.

## Research controls

`researchMode` controls how identification handles inconclusive evidence:

| Value | Behavior |
| - | - |
| `auto` | Chooses catalog-guided research only when independent evidence supports a focused candidate set. |
| `catalog_guided` | Researches the supplied catalog evidence. |
| `open_world` | Researches without trusting upstream catalog candidate claims. |

`researchProfile` accepts `quick` or `thorough`. Use `quick` for an interactive scan. Use `thorough` when a slower, broader search is acceptable.

## Billing responses

Paid routes reserve a configured maximum before calling the matching service and settle the actual measured usage afterward. Failed downstream work releases the reservation.

Successful paid responses include `x-boxpressd-ai-credits-remaining`. Handle these billing errors before prompting the user to retry:

| Status | Error | Action |
| - | - | - |
| `400` | `idempotency_key_required` | Supply a valid `Idempotency-Key`. |
| `402` | `ai_credits_exhausted` | Send the account owner to the returned billing URL or Developer Dashboard. |
| `403` | `ai_entitlement_required` | Purchase or enable AI access for the application. |
| `409` | `ai_request_replayed` | Treat the logical operation as already submitted and use a new key only for new work. |
| `503` | `match_service_unavailable` | Retry later with the same idempotency key. |

See [Credits and billing](/developer-api/billing) for balance and usage endpoints.
