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

# POST /offers/catalog

> Endpoint contract for POST /offers/catalog: fetch every offer your app may serve to one user, for client-side curation.

Returns every offer your app is entitled to serve **and** that the end user can redeem, each with its creatives and (on the serve path) a tokenized per-creative `clickUrl`. You curate and order the result client-side.

```http theme={null}
POST https://api.encorekit.com/encore/publisher/sdk/v1/offers/catalog
```

<Note>
  For the curation pattern (config-time inventory pull, per-render serve call, client-side fallback), see the [Curated Catalog guide](/publishers/offers-api/guides/curated-catalog). If the offer should appear at a moment with per-offer exposure measurement, use [`POST /offers/feed`](/publishers/offers-api/reference/feed) instead; the catalog has no per-offer `impressionUid` or delivered confirmation.
</Note>

## Authentication

Pass your publishable key in the `X-API-Key` header (`pk_live_*` for production, `pk_test_*` for development). Multi-app projects also send `X-Platform: api`. Full model: [overview's Authentication section](/publishers/offers-api/overview#authentication).

## Request

### Structure

```typescript theme={null}
interface OffersCatalogRequest {
  userId?: string;            // REQUIRED on the serve path: stable end-user identifier
  clientId?: string;          // operator/sub-publisher analytics dimension; defaults to userId
  countryCode?: string;       // ISO 3166-1 alpha-2; or forward the user's IP instead
  includeAllRegions?: boolean; // CONFIG-TIME ONLY: skip geo, return full inventory, no clickUrls
  limit?: number;             // 1-100; omit to return ALL eligible offers (the default)
  offset?: number;            // page offset, default 0; only meaningful with limit
}
```

### Field Reference

| Field               | Type    | Required | Description                                                                                                                                                                                                                                                                                                                        |
| ------------------- | ------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `userId`            | string  | On serve | Stable identifier for the end user you are serving. Bound into each creative's `clickUrl` so a tap records an attributed transaction for that user, and drives per-user protections at tap time. Use the same value across renders for the same user. Not used in config mode (`includeAllRegions`), which returns no `clickUrl`s. |
| `clientId`          | string  | No       | Identifier for the operator or sub-publisher on whose behalf offers are shown. Carried through as an analytics dimension. Defaults to `userId` when omitted.                                                                                                                                                                       |
| `countryCode`       | string  | See note | End user's country, ISO 3166-1 alpha-2, two uppercase letters. Overrides IP-based detection. Geography is required on the serve path: send this field, or forward the end user's IP via `X-Forwarded-For`. If neither resolves, the request is a `400`. Ignored when `includeAllRegions` is set.                                   |
| `includeAllRegions` | boolean | No       | **Config-time only.** Skips geography filtering and returns your full addressable inventory (entitlement still applies), with each offer's `targetCountries`. Returns no `clickUrl`s and needs no `userId`. Never render offers from this view to end users.                                                                       |
| `limit`             | integer | No       | Page size, `1` to `100`. Omit to return **all** eligible offers (the default); catalogs are small enough that you normally take the whole set and curate client-side.                                                                                                                                                              |
| `offset`            | integer | No       | Page offset (default `0`). Only meaningful together with `limit`.                                                                                                                                                                                                                                                                  |

## Response

### Structure

```typescript theme={null}
interface OffersCatalogResponse {
  success: boolean;
  offers: Array<{
    id: string;                     // offer (campaign) id; the key you curate by
    name: string;                   // offer name
    perk: string | null;            // value proposition
    badgeLabel: 'free_trial' | 'discount' | null;  // constrained promo badge
    oldPrice: string | null;        // display price before the offer
    newPrice: string | null;        // display price with the offer
    targetCountries: string[];      // where this offer converts (ISO 3166-1 alpha-2)
    organization?: {                // the advertiser
      id: string;
      name: string;
      description: string | null;
      url: string | null;
      logoUrl: string | null;
    };
    creatives?: Array<{
      id: string;
      title: string;
      subtitle: string | null;
      description: string | null;
      primaryImageUrl: string | null;
      logoUrl: string | null;
      additionalImages: string[];
      ctaText: string;
      supportedPlatforms: string[]; // 'ios' | 'android' | 'web'
      clickUrl?: string;            // tokenized click URL; serve responses only
    }>;
  }>;
  metadata: {
    total: number;    // offers matching after entitlement + geo (before pagination)
    limit: number;
    offset: number;
    hasMore: boolean; // more offers remain beyond this page
  };
  locale?: string;    // resolved locale served in this response (e.g. 'es')
}
```

### Field Reference

| Field                           | Type   | Description                                                                                                                                                                                                                                                                                                                                                                                                             |
| ------------------------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `offers`                        | array  | Offers your app is entitled to and (unless `includeAllRegions`) that are redeemable in the resolved end-user country, each with its active creatives. Filter to your curated ids and order client-side.                                                                                                                                                                                                                 |
| `offers[].id`                   | UUID   | The offer id. Save the ids of the offers you want to feature at config time, then filter serve responses by them.                                                                                                                                                                                                                                                                                                       |
| `offers[].targetCountries`      | array  | Countries where this offer converts. Populated so config-time curation can see each offer's coverage.                                                                                                                                                                                                                                                                                                                   |
| `offers[].organization`         | object | The advertiser: `id`, `name`, `description`, `url`, `logoUrl`.                                                                                                                                                                                                                                                                                                                                                          |
| `offers[].creatives[]`          | array  | Render fields per creative: `title`, `subtitle`, `description`, `primaryImageUrl`, `logoUrl`, `additionalImages`, `ctaText`, plus `supportedPlatforms` for curating to the end user's surface when you serve a mixed-platform audience.                                                                                                                                                                                 |
| `offers[].creatives[].clickUrl` | URL    | Tokenized click URL for **this** creative, of the form `https://api.encorekit.com/encore/click/<token>`. Send the end user here on tap: it records a transaction attributed to your app, this offer, this creative, and the `userId` you supplied, then 302-redirects to the advertiser. Treat it as opaque; never modify, wrap, or rebuild it. Present on serve responses; **absent** when `includeAllRegions` is set. |
| `metadata`                      | object | `total` (eligible offers before pagination), `limit`, `offset`, `hasMore`.                                                                                                                                                                                                                                                                                                                                              |
| `locale`                        | string | Resolved locale served in this response (language-only, for example `'es'`).                                                                                                                                                                                                                                                                                                                                            |

The response deliberately excludes Encore's internal and commercial fields (payout amounts, raw advertiser destination URLs, tracking parameters, internal notes and configuration). To send a user to an offer you always use the `clickUrl`, never a raw link.

## Example

```bash theme={null}
# Serve path: a real end user, geo-filtered. X-Platform only for multi-app projects.
curl -X POST https://api.encorekit.com/encore/publisher/sdk/v1/offers/catalog \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "X-Platform: api" \
  -H "Content-Type: application/json" \
  -d '{ "userId": "user-8b3f7c10", "countryCode": "US" }'
```

```json theme={null}
{
  "success": true,
  "offers": [
    {
      "id": "b1a7c0de-0000-4000-8000-000000000001",
      "name": "Apple Music - 3 months free",
      "perk": "3 months of Apple Music free",
      "badgeLabel": "free_trial",
      "oldPrice": "10.99",
      "newPrice": "0.00",
      "targetCountries": ["US", "CA"],
      "organization": {
        "id": "a1b2c3d4-0000-4000-8000-000000000001",
        "name": "Apple",
        "description": "Music, TV, and more.",
        "url": "https://www.apple.com",
        "logoUrl": "https://cdn.encorekit.com/org/apple/logo.png"
      },
      "creatives": [
        {
          "id": "c1d2e3f4-0000-4000-8000-000000000001",
          "title": "Get 3 months of Apple Music free",
          "subtitle": "Limited-time offer",
          "description": "For new subscribers.",
          "primaryImageUrl": "https://cdn.encorekit.com/creative/hero.png",
          "logoUrl": "https://cdn.encorekit.com/creative/logo.png",
          "additionalImages": ["https://cdn.encorekit.com/creative/alt.png"],
          "ctaText": "Claim Offer",
          "supportedPlatforms": ["ios", "android", "web"],
          "clickUrl": "https://api.encorekit.com/encore/click/eyJhcHBJZCI6..."
        }
      ]
    }
  ],
  "metadata": { "total": 8, "limit": 8, "offset": 0, "hasMore": false },
  "locale": "en"
}
```

Config-time inventory pull (no geo, no `clickUrl`s):

```bash theme={null}
curl -X POST https://api.encorekit.com/encore/publisher/sdk/v1/offers/catalog \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "includeAllRegions": true }'
```

## Status codes

| Code  | Meaning                                                                                                                                                                          |
| ----- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `200` | Success. `offers` may be empty when nothing is eligible.                                                                                                                         |
| `400` | Malformed request, a serve-path call missing `userId`, or no geography signal on the serve path (no `countryCode` and no resolvable forwarded IP). Don't retry; fix the request. |
| `401` | Missing or invalid `X-API-Key`.                                                                                                                                                  |
| `429` | Rate-limited. Back off and retry.                                                                                                                                                |
| `5xx` | Encore-side failure. Render nothing and proceed without offers.                                                                                                                  |

## Related

* [Curated Catalog guide](/publishers/offers-api/guides/curated-catalog): the config-time + serve-time curation pattern.
* [`POST /offers/feed`](/publishers/offers-api/reference/feed): ranked selection with per-offer exposure measurement.
* [API Reference overview](/publishers/offers-api/overview#choosing-a-selection-mode): choosing between feed and catalog.
