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

# Agent briefing

> Everything an AI agent or code generator needs to call the Offergrid API correctly

A single-page briefing for AI agents, code generators, and anyone building an
integration in one sitting. Everything here is stated explicitly rather than
implied, and every claim reflects the API as it is today.

## Essentials

|                   |                                                                      |
| ----------------- | -------------------------------------------------------------------- |
| **Base URL**      | `https://api.offergrid.io`                                           |
| **Protocol**      | HTTPS only                                                           |
| **Auth**          | `x-api-key: YOUR_TEAM_API_KEY` header on every authenticated request |
| **Auth scheme**   | API key in header. Not OAuth, not a bearer token, not Basic.         |
| **Content type**  | `application/json`                                                   |
| **Versioning**    | Version 1. No version segment in the URL, no version header.         |
| **Docs index**    | [`/docs/llms.txt`](https://offergrid.io/docs/llms.txt)               |
| **Full docs**     | [`/docs/llms-full.txt`](https://offergrid.io/docs/llms-full.txt)     |
| **Markdown twin** | Append `.md` to any docs URL                                         |

## OpenAPI specs

Stable URLs, regenerated from the API source on every change.

| Spec     | URL                                                                                              | Auth        |
| -------- | ------------------------------------------------------------------------------------------------ | ----------- |
| Provider | [`/docs/openapi/openapi-provider.json`](https://offergrid.io/docs/openapi/openapi-provider.json) | `x-api-key` |
| Reseller | [`/docs/openapi/openapi-reseller.json`](https://offergrid.io/docs/openapi/openapi-reseller.json) | `x-api-key` |
| Public   | [`/docs/openapi/openapi-public.json`](https://offergrid.io/docs/openapi/openapi-public.json)     | none        |
| Full     | [`/docs/openapi/openapi.json`](https://offergrid.io/docs/openapi/openapi.json)                   | `x-api-key` |

Generate a typed client from these rather than hand-writing request code.

## Roles decide what you can call

A team is a **provider**, a **reseller**, or **hybrid**, and the API key carries
that role. Calling the wrong family of endpoints returns `403`, not `404`.

* **Provider** → `/provider/*` — publish offers, fulfill orders, manage markets,
  webhooks, and brands.
* **Reseller** → `/reseller/*` — browse the catalog, check address availability,
  place and track orders, manage links, customers, and webhooks.
* **Hybrid** → both, with the same key.
* **Public** → `/public/*` — no key at all.

Full endpoint tables: [Provider](/docs/api-reference/provider) ·
[Reseller](/docs/api-reference/reseller) · [Public](/docs/api-reference/public)

## Minimal working request

```bash theme={null}
curl https://api.offergrid.io/reseller/catalog \
  -H "x-api-key: $OFFERGRID_API_KEY"
```

```typescript theme={null}
const response = await fetch('https://api.offergrid.io/reseller/catalog', {
  headers: { 'x-api-key': process.env.OFFERGRID_API_KEY! },
});
if (!response.ok) throw new Error(`Offergrid ${response.status}`);
const offers = await response.json();
```

## Gotchas

These are the things that most often make a first integration fail. None of them
are inferable from the endpoint list.

**Unknown request fields are rejected.** Sending a property an endpoint does not
define returns `400` with `"property <name> should not exist"`. You cannot take
a response object and `PATCH` it back — send only the fields you are changing.

**List endpoints are not paginated.** They return a complete array. There are no
`page`, `limit`, `offset`, or `cursor` parameters, and adding one is a `400`.
Filter server-side with the documented query parameters instead.

**Money is a string, not a number.** `"monthlyPrice": "79.99"`. Parse with a
decimal library — `parseFloat` introduces rounding drift into customer-visible
totals.

**`message` is not always a string.** On body-validation failures it is an array
of strings. Normalize before displaying.

**404 covers authorization on resources.** Another team's offer or order returns
`404`, not `403`. `403` means your team *role* is wrong for that endpoint family.

**There is no idempotency key.** `POST /reseller/orders` is not idempotent — two
identical requests create two orders. If a write times out, reconcile with
`GET /reseller/orders` before retrying.

**No rate limits on the authenticated API today.** Only the two public `/shop`
write endpoints are limited (per IP, per minute). Handle `429` anyway; do not
build a tight polling loop.

**Webhook signatures have no separate timestamp header.** The timestamp is the
`t=` component of `X-Offergrid-Signature`, and the signed string is
`` `${t}.${rawBody}` ``. Sign the *raw* body — re-serializing breaks the
signature. See [Verifying signatures](/docs/providers/webhooks#verifying-signatures).

**Both roles have webhooks, on separate paths.** `POST /provider/webhooks` and
`POST /reseller/webhooks`. Same four event types, same signed envelope — a
provider receives events scoped to its own offers, a reseller to the orders its
team placed. A hybrid team's single webhook covers both and fires once per
event.

**Offers carry a `serviceType` discriminator.** Branch on it (`"electricity"`,
`"internet"`, …) rather than probing for the grouped `electricity` / `internet`
objects, which are absent when they do not apply.

## Error handling

| Code  | Retryable | Meaning                                                     |
| ----- | --------- | ----------------------------------------------------------- |
| `400` | No        | Request body or query failed validation                     |
| `401` | No        | Missing, malformed, or revoked API key                      |
| `403` | No        | Valid key, wrong team role for this endpoint                |
| `404` | No        | Not found, or not yours                                     |
| `409` | No        | Conflicts with existing state (duplicate SKU or brand name) |
| `429` | Yes       | Rate limited — public endpoints only                        |
| `500` | Yes       | Server error; safe to retry idempotent requests             |

Full detail, including a retry-safe client: [Errors](/docs/api-reference/errors).

## Where to read next

| If you want to            | Read                                                                                                        |
| ------------------------- | ----------------------------------------------------------------------------------------------------------- |
| Make your first call      | [Quickstart](/docs/quickstart)                                                                                   |
| Understand keys and roles | [Authentication](/docs/api-reference/authentication)                                                             |
| Know the shared rules     | [API conventions](/docs/api-reference/conventions)                                                               |
| Handle failures           | [Errors](/docs/api-reference/errors)                                                                             |
| Receive events            | [Provider webhooks](/docs/providers/webhooks) · [Reseller webhooks](/docs/resellers/webhooks)                         |
| Know what can change      | [Versioning](/docs/api-reference/versioning)                                                                     |
| See every endpoint        | [Provider](/docs/api-reference/provider) · [Reseller](/docs/api-reference/reseller) · [Public](/docs/api-reference/public) |

Something unclear or wrong? Email [support@offergrid.io](mailto:support@offergrid.io).
