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

# Troubleshooting and frequently asked questions

> Solutions and FAQs for common Yoshi API issues, including authentication errors, rate limiting, webhook delivery failures, and transaction data quirks.

## Authentication

### 401 Unauthorized

**"Invalid, expired, or missing API key"**

* Confirm your key starts with `yoshi_`
* Check that the `Authorization` header uses the `Bearer` scheme: `Authorization: Bearer yoshi_YOUR_KEY`
* Verify the key hasn't been revoked in your [dashboard](https://beta.yoshi.ai/settings)
* Make sure there are no extra spaces or newlines in the key

```bash theme={null}
# Correct
curl https://api.yoshi.ai/accounts \
  -H "Authorization: Bearer yoshi_3xK9mP..."

# Wrong — missing "Bearer"
curl https://api.yoshi.ai/accounts \
  -H "Authorization: yoshi_3xK9mP..."
```

## Rate limiting

### 429 Too Many Requests

**"Rate limit exceeded"**

Check the response headers to see your current limit and when it resets:

```
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1712764800
Retry-After: 43
```

Wait `Retry-After` seconds before retrying. Implement exponential backoff for automated systems:

```typescript theme={null}
if (response.status === 429) {
  const retryAfter = Number(response.headers.get("Retry-After")) || 60;
  await new Promise((r) => setTimeout(r, retryAfter * 1000));
}
```

<Tip>
  Use [webhooks](/webhooks/overview) to replace polling. Every webhook you process is one fewer API call.
</Tip>

## Webhooks

### Not receiving events

* **URL must be HTTPS.** HTTP endpoints are rejected.
* **Return `2xx` within 15 seconds.** If your handler times out, the event is marked as failed and retried.
* **Check the portal.** Open the [self-service portal](/webhooks/overview#consumer-portal) to see delivery attempts, response codes, and retry status.
* **Check event filters.** If you specified `filter_types` when creating the endpoint, only those events are delivered.

### Signature verification failing

* Use the **raw request body** for verification, not a parsed JSON object. Parsing and re-serializing changes whitespace and key order, which breaks the signature.
* Verify the timestamp is within your tolerance window (default: 5 minutes).
* Make sure you're using the correct signing secret for this endpoint.

See [Signature Verification](/webhooks/verification) for framework-specific examples.

## Transactions

### Duplicate transactions

Transactions can appear twice during the pending-to-posted transition. A pending transaction and its posted version may coexist briefly until the pending record clears.

Always use the transaction `id` for deduplication — don't match on amount, date, or description.

### Amount has unexpected sign

The `amount` field follows accounting convention:

* On a **credit card** account, a purchase (outflow) has a **positive** amount
* On a **checking** account, a purchase (outflow) has a **negative** amount

Use `cash_flow_direction` for logic and `amount_absolute` for display:

```typescript theme={null}
// Display
const label = `${tx.cash_flow_direction === "outflow" ? "-" : "+"}$${tx.amount_absolute}`;

// Logic
if (tx.cash_flow_direction === "outflow" && tx.amount_absolute > 100) {
  // Large purchase
}
```

### `date_posted` vs `date_authorized`

| Field             | Use when                                                                                    |
| ----------------- | ------------------------------------------------------------------------------------------- |
| `date_posted`     | Reporting, accounting, reconciliation. This is the final settlement date.                   |
| `date_authorized` | Showing when the user actually made the purchase. May be `null` for some transaction types. |

## Data freshness

### Stale data / `as_of` timestamp

Yoshi syncs data from your connected institutions on a recurring schedule. The `as_of` field on each account tells you when data was last refreshed.

If `as_of` is more than a few hours old:

* The institution may sync less frequently (some update once per business day)
* There may be a temporary connection issue

Show users the `as_of` timestamp so they know how current the data is. Do not promise real-time accuracy.

### Missing recent transactions

Transactions appear after the next sync cycle, not instantly. If a user just made a purchase:

1. It won't appear until the next sync
2. It may appear as `pending` first
3. It settles as `posted` within 1–3 business days

## Pagination

### Cursor returns the same data

* Make sure you're passing the `cursor` from the **most recent** response, not a cached cursor.
* Don't store cursors between sessions — they're designed for sequential traversal.
* If a cursor becomes invalid, the API returns `400`. Restart pagination without a cursor.

See [Pagination](/concepts/pagination) for the full guide.

## Still stuck?

Include the `request_id` from the error response when reaching out for support. Every API response includes it in the `meta` object and as an `X-Request-Id` header:

```json theme={null}
{
  "meta": {
    "request_id": "req_a1b2c3d4-e5f6-7890-abcd-ef1234567890"
  }
}
```
