<!-- FILE: https://developer.kyriba.com/static/llm/skills/kyriba-guide/SKILL.md -->

# Kyriba Integration Guide — Rate Limits, Best Practices & Patterns

> This file is required reading before writing any Kyriba integration code.
> It supplements [kyriba/SKILL.md](https://developer.kyriba.com/static/llm/skills/kyriba/SKILL.md) with the technical rules needed to produce correct, production-safe implementations.

---

## API Usage Limits

### Rate & Concurrency — per API client

| Limit | Value |
|---|---|
| Rate | 50 calls / 30 seconds |
| Concurrency | 1 active request at a time |
| Upload | 10 MB per request |
| Download | 100 MB per request |

On **HTTP 429:** read `Kyriba-Customer-Rate-Limit-Reset` (epoch ms), wait until that time, then retry.

### Daily Quota — per customer environment

| API calls / day | Mode | Rate limit |
|---|---|---|
| 0 – 15,000 | Standard | 50 calls / 30s |
| 15,001 – 20,000 | Overuse | 25 calls / 30s |
| 20,001+ | **Blocked** | Until daily reset |

Quota is shared across all API clients in the environment.

### Response Headers to Monitor

| Header | Description |
|---|---|
| `Kyriba-Customer-Rate-Limit-Remaining` | Calls left in current 30s window |
| `Kyriba-Customer-Rate-Limit-Reset` | Epoch ms when 30s window resets |
| `Kyriba-Customer-Quota-Remaining` | Calls left today |
| `Kyriba-Customer-Quota-Reset` | Epoch ms when daily quota resets |
| `Kyriba-Customer-Extended-Quota-Remaining` | Extra calls available in overuse period |

### Deprecation Policy

Deprecated APIs stay available for at least **6 months** from their News announcement before retirement. Deprecated APIs are flagged in the API Catalog.

---

## Pagination

| Parameter | Default | Max |
|---|---|---|
| `limit` | 100 | 1,000 |
| `offset` | 0 | — |

**Stop condition:** stop when `len(results) < limit`. Do not rely on `metadata.total` — unreliable on some endpoints. Some APIs use non-standard field names (`pageLimit`, `pageResults`) — check the domain skill.

```python
offset, limit, results = 0, 100, []
while True:
    page = GET(url, params={"limit": limit, "offset": offset}).results
    results.extend(page)
    if len(page) < limit:
        break
    offset += limit
```

---

## RSQL Filtering

All list endpoints accept a `?filter=` or `?q=` parameter using RSQL syntax.

| Operator | Meaning | Example |
|---|---|---|
| `==` | equals | `currency.code==EUR` |
| `!=` | not equals | `status!=CLOSED` |
| `=in=` | in list | `status=in=(ACTIVE,PENDING)` |
| `=out=` | not in list | `currency=out=(JPY)` |
| `==ABC*` | starts with | `code==COMP*` |
| `=gt=` / `=lt=` | greater / less than | `amount=gt=1000` |
| `=ge=` / `=le=` | ≥ / ≤ | `date=ge=2026-01-01` |
| `;` | AND | `currency==EUR;status==ACTIVE` |
| `,` | OR | `code==ACC_FR,code==ACC_DE` |

Always URL-encode the `filter` value.

---

## Sorting

```
?sort=fieldName        # ascending
?sort=-fieldName       # descending
?sort=field1,-field2   # multi-field
```

---

## Common Integration Patterns

### Data Import via Process Template

```
1. POST /api/v1/data?fileName={name}                              → fileId
2. POST /api/v1/process-templates/{ref}/run?fileIds={id}          → taskId
3. GET  /api/v1/process-templates/{ref}/executions/{taskId}       → poll every 5s
```

Status lifecycle: `Pending` → `In progress` → `Complete` | `Warning` | `Failed`  
`Warning` = completed with some rejected records — treat as partial success.

### Polling Pattern

- Interval: 5 seconds · Max attempts: 30
- Stop on: `Complete`, `Warning`, or `Failed`

### Webhook Validation

1. Kyriba POSTs to your URL with `kyriba-webhook-payload-signature` header
2. Validate: `HMAC-SHA256(secret, request_body)` must match the signature
3. Return `202 Accepted` within 10 seconds — process asynchronously
4. Deduplicate using `eventId` — Kyriba retries with exponential backoff up to 24 hours

### Date Range APIs

Bank statement balances and cash balances accept a maximum **31-day range** per request. Use rolling windows for longer periods.

### Bank Transactions

No dedicated bank transactions API. Two options:
- **Process Template** — run the "Export bank actuals" template, download result as file
- **Cash Flows API** — reconciled transactions appear with `status = "ACTUAL"`