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

# Production integration

> Build a resilient Yir client with idempotent submission, bounded polling, safe retries, and explicit failure handling.

<Info>
  This guide reflects the current developer-preview contract: GPT Image 2 text-to-image through the certified KIE-compatible and APIMart-compatible routes. It does not promise unreleased models or arbitrary provider forwarding.
</Info>

<CardGroup cols={3}>
  <Card title="Authenticate safely" icon="key-round" href="/getting-started/authentication">
    Keep the Yir API key on the server and send it only as a Bearer credential.
  </Card>

  <Card title="Track one logical request" icon="fingerprint" href="#submission-contract">
    Persist the idempotency key and task ID before polling.
  </Card>

  <Card title="Handle uncertain outcomes" icon="refresh-cw" href="#polling-and-recovery">
    Retry queries without turning a timeout into a second generation request.
  </Card>
</CardGroup>

## Submission contract

<Steps>
  <Step title="Create an idempotency key">
    Generate one opaque value for one logical generation request. Reuse it only when retrying the identical request.
  </Step>

  <Step title="Submit through one compatible surface">
    Choose the KIE-compatible or APIMart-compatible shape your application already understands. Do not send an upstream provider credential.
  </Step>

  <Step title="Persist the returned task ID">
    Store the task ID with your local request record before starting background polling.
  </Step>

  <Step title="Poll to a terminal state">
    Use a bounded interval and an overall application deadline. Continue querying the same task after transient transport failures.
  </Step>
</Steps>

<Warning>
  Never generate a fresh idempotency key merely because the submit response timed out. The original request may already have been accepted and may still create billing facts.
</Warning>

### Required headers

<ParamField header="Authorization" type="string" required>
  A Yir credential in the form `Bearer $YIR_API_KEY`.
</ParamField>

<ParamField header="Idempotency-Key" type="string" required>
  A caller-generated opaque identifier for the logical request. The same value with different request data is rejected.
</ParamField>

### Submit from your backend

<CodeGroup>
  ```bash cURL theme={null}
  curl --request POST \
    --url "https://gateway.yir.ai/apimart/v1/images/generations" \
    --header "Authorization: Bearer $YIR_API_KEY" \
    --header "Content-Type: application/json" \
    --header "Idempotency-Key: $YIR_IDEMPOTENCY_KEY" \
    --data '{
      "model": "gpt-image-2",
      "prompt": "A quiet observatory above a sea of clouds",
      "size": "1:1",
      "resolution": "1k",
      "n": 1
    }'
  ```

  ```typescript TypeScript theme={null}
  const response = await fetch(
    "https://gateway.yir.ai/apimart/v1/images/generations",
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.YIR_API_KEY}`,
        "Content-Type": "application/json",
        "Idempotency-Key": crypto.randomUUID(),
      },
      body: JSON.stringify({
        model: "gpt-image-2",
        prompt: "A quiet observatory above a sea of clouds",
        size: "1:1",
        resolution: "1k",
        n: 1,
      }),
    },
  );

  if (!response.ok) throw new Error(`Yir rejected the request: ${response.status}`);
  const task = await response.json();
  ```

  ```python Python theme={null}
  import os
  import uuid
  import requests

  response = requests.post(
      "https://gateway.yir.ai/apimart/v1/images/generations",
      headers={
          "Authorization": f"Bearer {os.environ['YIR_API_KEY']}",
          "Idempotency-Key": str(uuid.uuid4()),
      },
      json={
          "model": "gpt-image-2",
          "prompt": "A quiet observatory above a sea of clouds",
          "size": "1:1",
          "resolution": "1k",
          "n": 1,
      },
      timeout=30,
  )
  response.raise_for_status()
  task = response.json()
  ```
</CodeGroup>

<ResponseField name="data[0].task_id" type="string" required>
  Stable APIMart-compatible task identifier to persist and query.
</ResponseField>

## Choose a compatible surface

<Tabs>
  <Tab title="KIE-compatible">
    Submit with `POST /kie/api/v1/jobs/createTask`, read `data.taskId`, and query `GET /kie/api/v1/jobs/recordInfo?taskId=...`.
  </Tab>

  <Tab title="APIMart-compatible">
    Submit with `POST /apimart/v1/images/generations`, read `data[0].task_id`, and query `GET /apimart/v1/tasks/{id}`.
  </Tab>
</Tabs>

```mermaid theme={null}
sequenceDiagram
  participant App as Your backend
  participant Yir as Yir Gateway
  participant Job as Asynchronous task
  App->>Yir: Submit + Bearer key + idempotency key
  Yir-->>App: Stable task ID
  loop bounded polling
    App->>Yir: Query the same task ID
    Yir->>Job: Read current facts
    Job-->>Yir: Active or terminal state
    Yir-->>App: Compatible status response
  end
```

## Polling and recovery

| Situation                      | Safe client behavior                                        |
| ------------------------------ | ----------------------------------------------------------- |
| Query returns an active status | Wait, then query the same task ID again.                    |
| Query request times out        | Treat the outcome as unknown and retry the query.           |
| Submit response times out      | Retry the identical submit with the same idempotency key.   |
| Terminal failure is returned   | Stop polling and handle the stable public error.            |
| Terminal success is returned   | Persist the result URL before the retention window expires. |

<AccordionGroup>
  <Accordion title="How fast should I poll?">
    Start conservatively, add jitter, and enforce an overall deadline in your application. A polling timeout is not proof that generation failed.
  </Accordion>

  <Accordion title="Should I submit a replacement when a task is slow?">
    No. Query the existing task ID. A second submit represents a second logical request unless it is an idempotent retry of identical data.
  </Accordion>

  <Accordion title="What may I include in support evidence?">
    Include the Yir task ID, source path, approximate UTC time, HTTP status, public error code, and a safe key prefix. Never include the complete key, prompt, source media, or raw provider response.
  </Accordion>
</AccordionGroup>

<Check>
  Your integration is ready for a controlled test when it persists both identifiers, reuses the idempotency key for submit retries, polls the same task ID, and never exposes the Yir API key to browser code.
</Check>
