# Request id আর debugging

> Tokens-এর প্রতিটা response-এ যে request id header থাকে, সেটা support-কে কীভাবে জানাবেন, কী log করবেন, curl দিয়ে fail হওয়া call কীভাবে আবার চালাবেন, আর error body ও usage page কীভাবে পড়বেন।

কোনো request fail করলে, বা খরচ আশার চেয়ে বেশি হলে আপনার দরকার ঠিক ওই call-টাকে আঙুল দিয়ে দেখানো। Tokens-এ প্রতিটা response-এ একটা request id থাকে, আর যে request-এর বিল হয়েছে তার জন্য usage page-এ একই id-সহ একটা row তৈরি হয়। এই পেজে দেখবেন id কোথায় পাবেন, তার আশপাশে আর কী log করবেন, app-এর failure কীভাবে এমন curl command বানাবেন যা যে কেউ চালাতে পারে, আর কী ঘটেছে তা বলে দেওয়া দুটো জায়গা (error body আর usage page) কীভাবে পড়বেন।

## Request id header

`/v1`-এর প্রতিটা response-এ, সফল হোক বা error, এই header-গুলো থাকে:

| Header                | Value                                                                                          |
| --------------------- | ---------------------------------------------------------------------------------------------- |
| `x-tokens-request-id` | এই request-এর জন্য gateway-র id (একটা UUID)। এটা সব সময় Tokens-ই বানায়। Support-কে এটাই জানাবেন। |
| `x-request-id`        | আপনি যে `x-request-id` পাঠিয়েছিলেন হুবহু সেটাই। না পাঠালে ওপরের id-টাই                          |
| `x-trace-id`          | সফল inference response-এ: `x-tokens-request-id`-এর মতোই একই মান                                |

Error body-তেও id-টা `request_id` নামে থাকে। `/v1/chat/completions`, `/v1/responses` আর OpenAI format-এর বাকি endpoint-এ সেটা `error`-এর ভেতরে থাকে। `/v1/messages` Anthropic-এর error format ব্যবহার করে, তাই সেখানে `request_id` থাকে body-র একদম ওপরের স্তরে, `error`-এর পাশে:

```json
{
  "error": {
    "message": "Prepaid wallet balance is insufficient for this request.",
    "type": "insufficient_quota",
    "code": "insufficient_credits",
    "param": null,
    "request_id": "8f0c7a4e-2b1d-4c55-9a51-3f7e2d9b6c10"
  }
}
```

```json
{
  "type": "error",
  "error": {
    "type": "billing_error",
    "message": "Prepaid wallet balance is insufficient for this request.",
    "code": "insufficient_credits"
  },
  "request_id": "8f0c7a4e-2b1d-4c55-9a51-3f7e2d9b6c10"
}
```

কয়েকটা কথা মনে রাখবেন:

- **Tokens provider-এর header পাস করে না।** শুধু content type, `cache-control`, `retry-after`, `x-request-id` আর `x-tokens-*` header আপনার কাছে পৌঁছায়। model-এর নিজের provider-এর request id কখনো দেখতে পাবেন না, তাই Tokens-এর id-টাই জানান।
- **Id আসে header-এর সাথেই।** Streaming-এ প্রথম token আসার আগেই এটা পাওয়া যায়। call শুরুর সময়ই log করে রাখুন, তাহলে stream পরে মাঝপথে ভেঙে গেলেও id আপনার হাতে থাকবে।
- **আপনার নিজের `x-request-id` যেমন পাঠিয়েছেন তেমনই ফেরত আসে।** এটা দিয়ে আপনার log-এর লাইনের সাথে Tokens-এর id মেলান। প্রতিটা attempt-এর জন্য নতুন একটা পাঠান, user-এর একটা action-এর জন্য একটা নয়। তাহলে retry আর তার প্রথম চেষ্টা আলাদা করে চেনা যায়।
- **আপনার দিকে timeout হলে Tokens-এর id পাবেন না,** কারণ কোনো response-ই আসেনি। এই কারণেই পাঠানোর আগে নিজের id-ও log করে রাখা উচিত।
- **SDK-র helper আপনার id দেখাতে পারে, আমাদেরটা নয়।** কিছু SDK `x-request-id` থেকে পড়া একটা `request_id` দেয়। আপনি নিজের `x-request-id` পাঠালে ওই মান আপনারই। gateway-র id পেতে raw header থেকে `x-tokens-request-id` পড়ুন।

## Code-এ id পড়ার উপায়

:::code-tabs

```bash title="cURL"
curl -sS -i https://tokens.bd/v1/chat/completions \
  -H "Authorization: Bearer $TOKENS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"deepseek/deepseek-v4.1-flash","messages":[{"role":"user","content":"ping"}],"max_tokens":20}' \
  | grep -i "^x-tokens-request-id"
```

```python title="Python"
import os
import openai
from openai import OpenAI

client = OpenAI(base_url="https://tokens.bd/v1", api_key=os.environ["TOKENS_API_KEY"])

try:
    raw = client.chat.completions.with_raw_response.create(
        model="deepseek/deepseek-v4.1-flash",
        messages=[{"role": "user", "content": "ping"}],
        max_tokens=20,
    )
    print("request id:", raw.headers.get("x-tokens-request-id"))
    completion = raw.parse()
except openai.APIStatusError as e:
    print("failed:", e.status_code, e.code, e.response.headers.get("x-tokens-request-id"))
    raise
```

```typescript title="Node.js"
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://tokens.bd/v1",
  apiKey: process.env.TOKENS_API_KEY,
});

try {
  const { data, response } = await client.chat.completions
    .create({
      model: "deepseek/deepseek-v4.1-flash",
      messages: [{ role: "user", content: "ping" }],
      max_tokens: 20,
    })
    .withResponse();
  console.log("request id:", response.headers.get("x-tokens-request-id"));
  console.log(data.choices[0]?.message.content);
} catch (err) {
  if (err instanceof OpenAI.APIError) {
    const headers = err.headers as Headers | Record<string, string | undefined> | undefined;
    const id = headers instanceof Headers ? headers.get("x-tokens-request-id") : headers?.["x-tokens-request-id"];
    console.error("failed:", err.status, err.code, id);
  }
  throw err;
}
```

:::

Anthropic SDK-র ক্ষেত্রেও একই header raw response-এ থাকে। সেখানে কীভাবে পৌঁছাবেন, তা প্রতিটা SDK-র নিজের docs-এ আছে। `fetch` বা `requests` দিয়ে call করলে response header সরাসরি পড়ুন।

## কী log করবেন

প্রতিটা attempt-এর জন্য একটা structured line-ই যথেষ্ট। response-এর header আসার সময় log করুন, আর পরে কিছু fail করলে আবার।

| Field                                       | কেন                                                                   |
| ------------------------------------------- | --------------------------------------------------------------------- |
| `x-tokens-request-id`                       | এটা দিয়ে support আপনার request খুঁজে পায়                              |
| আপনার নিজের request id                      | call-টাকে আপনার log আর retry-র ধারার সাথে বেঁধে রাখে                   |
| সময়, time zone সহ (UTC)                    | id না থাকলে এটাই ভরসা                                                 |
| Endpoint আর model                           | `chat/completions`, `messages`, আর model id হুবহু                     |
| HTTP status আর `error.code`                 | কী ঘটেছে। branch করবেন code দেখে, message দেখে নয়                     |
| Latency, আর stream হলে প্রথম token আসতে সময় | provider ধীর না আপনার client ধীর, তা আলাদা করা যায়                    |
| Response-এর `usage`                         | input token, cached token, output token, যাতে খরচ ব্যাখ্যা করতে পারেন   |
| Attempt নম্বর                               | retry চেনা যায়                                                       |
| কোন key (নাম, secret কখনোই নয়)             | একাধিক key থাকলে সঠিকটা খুঁজে পাওয়া যায়                              |

যা log করবেন না:

- **API key,** কোনোভাবেই নয়। header-এ নয়, dump করা config-এও নয়।
- **পুরো prompt আর উত্তর, ডিফল্ট হিসেবে।** এতে আপনার customer-এর data থাকতে পারে। Tokens নিজেও prompt-এর content রাখে না, রাখে request id ধরে usage metadata, তাই request খুঁজে পেতে id-ই যথেষ্ট। debug-এর জন্য body log করলে retention ছোট রাখুন আর স্পর্শকাতর অংশ মুছে দিন।

## Support-কে id জানানোর নিয়ম

[Support](/dashboard/support)-এ ticket খোলার সময় এগুলো দিন:

1. `x-tokens-request-id`। সমস্যাটা বারবার হলে একাধিক id।
2. কখন ঘটেছে, time zone সহ।
3. Endpoint আর model id।
4. HTTP status আর `error.code`। বিলের প্রশ্ন হলে কত কাটা উচিত ছিল বলে আপনি আশা করেছিলেন।
5. সমস্যাটা আবার ঘটানো যায় কি না, আর হাতে থাকলে curl command (পরের অংশে আছে)।

API key কখনো দেবেন না। Id থেকেই support আপনার call-এর usage metadata খুঁজে পায়। বাকি প্রক্রিয়া [সাহায্য পাওয়ার](/docs/support) পেজে আছে, আর সবার জন্যই কিছু ভাঙা কি না তা [status page](/status)-এ দেখা যায়।

## curl দিয়ে fail হওয়া request আবার চালান

App-এ request fail করলে app-টাকে ছবি থেকে সরিয়ে দিন। একই রকম fail করা একটা curl command থেকে বোঝা যায় সমস্যাটা আপনার code, configuration, key না অ্যাকাউন্টে।

1. **App যে body পাঠিয়েছিল হুবহু সেটা ধরুন:** `model`, `messages`, `max_tokens`, tools আর বাকি সবকিছুর JSON। যে customer-এর লেখা অন্যকে দেখাতে চান না, সেটা মুছে দিন।
2. **একটা file-এ save করুন** আর একই key ও একই endpoint দিয়ে পাঠান।
3. **Header আর body আলাদা file-এ রাখুন,** যাতে id আর error দুটোই থেকে যায়।

:::code-tabs

```bash title="Chat completions"
curl -sS -D headers.txt -o response.json -w "HTTP %{http_code}\n" \
  https://tokens.bd/v1/chat/completions \
  -H "Authorization: Bearer $TOKENS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "x-request-id: repro-$(date +%s)" \
  -d @body.json

grep -i "^x-tokens-request-id" headers.txt
jq '.error // .' response.json
```

```bash title="Messages"
curl -sS -D headers.txt -o response.json -w "HTTP %{http_code}\n" \
  https://tokens.bd/v1/messages \
  -H "x-api-key: $TOKENS_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -H "x-request-id: repro-$(date +%s)" \
  -d @body.json

grep -i "^x-tokens-request-id" headers.txt
jq '.error // .' response.json
```

```powershell title="Windows PowerShell"
curl.exe -sS -D headers.txt -o response.json -w "HTTP %{http_code}`n" `
  https://tokens.bd/v1/chat/completions `
  -H "Authorization: Bearer $env:TOKENS_API_KEY" `
  -H "Content-Type: application/json" `
  -d "@body.json"

Select-String -Path headers.txt -Pattern "x-tokens-request-id"
Get-Content response.json
```

:::

Windows PowerShell 5.1-এ `curl` আসলে অন্য একটা command-এর alias, তাই `curl.exe` লিখুন। Streaming উত্তর আসতে আসতে দেখতে চাইলে `-N` দিন আর body-তে `"stream": true` যোগ করুন।

এরপর একবারে একটা জিনিস বদলে সমস্যা ছোট করে আনুন:

| curl call যদি...                         | তাহলে বুঝবেন                                                                                       |
| ---------------------------------------- | -------------------------------------------------------------------------------------------------- |
| একই রকম fail করে                         | সমস্যা request-এ, key-তে বা অ্যাকাউন্টে। `error.code` পড়ে [errors](/docs/errors) পেজে খুঁজুন        |
| ঠিকঠাক চলে                               | সমস্যা আপনার app-এ: environment-এ অন্য key, বদলে যাওয়া body, কোনো proxy, timeout, বা library-র default |
| `stream` ছাড়া চলে, দিলে fail করে         | মাঝখানে buffering করা কোনো proxy, নয়তো SSE সামলাতে না পারা client। দেখুন [streaming](/docs/streaming) |
| শুধু একটা নির্দিষ্ট model-এ fail করে      | ওই model-এর parameter বা availability-র সমস্যা। `GET /v1/models` থেকে অন্য একটা চেষ্টা করুন           |
| শুধু tools থাকলে বা body বড় হলে fail করে | tool schema, 10 MB body-র সীমা, বা model-এর context window                                          |
| মাঝে মাঝে fail করে                       | rate limit, নয়তো provider-এর সমস্যা। `Retry-After` আর [status page](/status) দেখুন                |

Key আর base URL ঠিক আছে কি না, তার দ্রুত পরীক্ষা হলো `GET /v1/models`। এতে কোনো model চলে না, আর এটা আপনার per-minute limit-এও গোনা হয় না।

## Error body কীভাবে পড়বেন

Branch করুন `error.code` দেখে। message মানুষের জন্য, ওটা বদলে যেতে পারে। Error-এর গড়ন আর code-গুলো [errors](/docs/errors) পেজে আছে। একটা error সংক্ষেপে এভাবে পড়ুন:

| অংশ                  | কীভাবে পড়বেন                                                                              |
| -------------------- | ------------------------------------------------------------------------------------------ |
| HTTP status          | শ্রেণি: 4xx মানে সমস্যা request, key বা অ্যাকাউন্টে, 5xx মানে gateway বা কোনো provider-এর    |
| `error.code`         | ঠিক কারণটা। `insufficient_credits`, `window_exhausted`, `model_not_found` ইত্যাদি           |
| `error.message`      | প্রসঙ্গ: যেমন key যেসব model ব্যবহার করতে পারে তার তালিকা, বা window কখন reset হবে            |
| `request_id`         | যেটা support-কে জানাবেন                                                                    |
| `Retry-After` header | 429-এ কত সেকেন্ড অপেক্ষা করবেন                                                             |

দুটো জিনিস নিয়ে অনেকে ধাঁধায় পড়েন:

- **Provider-এর error সাধারণ message হয়ে আসে।** model-এর provider কোনো request reject করলে বা fail করলে, provider-এর ভেতরের তথ্য ফাঁস না হওয়ার জন্য message বদলে একটা সাধারণ message বসানো হয়। তবে status আর `code` থেকে কোন ধরনের failure তা বোঝা যায়। provider থেকে 400 এলে আপনার parameter-গুলো দেখুন, আর model সেগুলো support করে কি না দেখুন।
- **200 পাওয়ার পরও stream fail করতে পারে।** Streaming শুরু হয়ে গেলে status আগেই 200 হয়ে যায়। provider মাঝপথে fail করলে connection বন্ধ হয়ে যায় `data: [DONE]` ছাড়াই (Messages-এ `message_stop` ছাড়াই), আর কোনো error body আসে না। finish reason ছাড়া শেষ হওয়া stream-কে অসম্পূর্ণ ধরুন, আর শুরুতে log করে রাখা request id কাজে লাগান।

## Usage page কীভাবে পড়বেন

[Usage](/dashboard/usage) পেজে আপনার সাম্প্রতিক request-গুলো activity table-এ আসে, সবচেয়ে নতুনটা ওপরে:

| Column   | কী দেখায়                                                                                       |
| -------- | ----------------------------------------------------------------------------------------------- |
| Time     | request কখন record হয়েছে                                                                       |
| Usage    | request-এর খরচ                                                                                  |
| Tokens   | `input · N cached · output`। cached অংশ শুধু থাকলেই দেখায়, আর সেটা cache read আর write মিলিয়ে    |
| Timing   | request-এ কত সময় লেগেছে                                                                        |
| Model    | আপনি যে model id দিয়ে call করেছেন                                                               |
| Mode     | `/v1` দিয়ে আসা request-এর জন্য `api`                                                           |
| Status   | বিল হওয়া request-এর জন্য `COMPLETED`                                                           |
| Trace ID | request id। ক্লিক করলে পুরো মান copy হয়; table-এ শুধু প্রথম আটটা অক্ষর দেখায়                    |

API request-এর Trace ID আর তার `x-tokens-request-id` একই মান। একটা request খুঁজতে হলে আপনার log থেকে id copy করে এই column-এ খুঁজুন। Table-এ page আছে, তাই পুরোনো request-এর জন্য নিচের per-page selector দিয়ে বেশি row দেখান।

এই পেজ থেকে কী জানা যায় আর কী যায় না:

- **শুধু বিল হওয়া request-ই আসে।** Request চলে বিল হয়ে গেলে তবেই gateway usage record করে। চলার আগেই reject হওয়া request (ভুল key, ব্যালান্স নেই, rate limit) বা provider-এ fail করা request-এর কোনো row নেই। এগুলোর ক্ষেত্রে আপনার নিজের log-এ রাখা request id আর error body-ই একমাত্র রেকর্ড।
- **আপনার cancel করা request-ও থাকে।** Stream-এর মাঝখানে আপনার client disconnect করলে input আর তত দূর পর্যন্ত তৈরি হওয়া output-এর বিল হয়, আর row-তে সেই সংখ্যাই দেখায়।
- **Estimated badge মানে usage report আসেনি।** Provider token-এর হিসাব না পাঠালে Tokens request আর উত্তরের আকার থেকে একটা হিসাব বের করে row-তে চিহ্ন দিয়ে দেয়। আপনার বিল হয় সেই হিসাব ধরেই।
- **Cached token কম খরচের কারণ হতে পারে।** লম্বা prompt-এও খরচ কম হলে Tokens column-এর cached অংশ দেখুন। দেখুন [prompt caching](/docs/prompt-caching)।
- **ছোট prompt-এ বড় খরচের কারণ সাধারণত এগুলোই:** প্রতিটা turn-এ আবার পাঠানো লম্বা কথোপকথনের history, যে model ব্যবহার করে তার জন্য বড় `max_tokens`, output হিসেবে গোনা reasoning token, একাধিকবার বিল হওয়া retry, বা loop থেকে আসা অনেক request। কোনটা, তা প্রতিটা request-এর token দেখলেই বোঝা যায়।

মোট হিসাব, দৈনিক chart আর plan window-গুলো ব্যাখ্যা করা আছে [usage and alerts](/docs/usage-and-alerts) পেজে। Code থেকে দেখে নিতে চাইলে `GET /v1/tokens/usage` আপনার window, ব্যালান্স আর key-এর cap ফেরত দেয়, এর জন্য কোনো বিল হয় না।

## Debugging-এর ছোট একটা রুটিন

1. `error.code` আর status পড়ুন। code-টা [errors](/docs/errors) পেজে খুঁজুন।
2. Response থেকে, বা আপনার log থেকে `x-tokens-request-id` নিন।
3. সেভ করা body দিয়ে curl-এ আবার চালান। একবারে একটা জিনিস বদলান।
4. একসাথে কয়েকটা request বা model fail করলে [status](/status) দেখুন।
5. প্রশ্নটা খরচ বা token নিয়ে হলে [Usage](/dashboard/usage) দেখুন।
6. তারপরও ব্যাখ্যা না মিললে id, সময়, model, status আর code দিয়ে ticket খুলুন।

---
Page: https://tokens.bd/bn/docs/request-ids-and-debugging
