> ## 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.

# GET /entitlements/server

> Endpoint contract for GET /entitlements/server: verify a user's entitlements from your backend, over HMAC-signed server-to-server auth.

Returns one user's accumulated entitlements, split into verified and provisional, so your
server can decide what to unlock. This is the pull counterpart to the
[Offer Completed webhook](/publishers/offers-api/reference/offer-completed-webhook): the
webhook pushes each completion to you as it verifies, this endpoint answers "what does
this user hold right now" on demand. It is the one server-to-server read an API
integration needs, because entitlements are server-authoritative: never gate real access
on anything a client reports.

```http theme={null}
GET https://api.encorekit.com/encore/publisher/sdk/v1/entitlements/server?userId=user-123
```

## Authentication

Unlike the offer endpoints, this endpoint is HMAC-signed. Three headers are required,
plus `X-Platform` for multi-app projects:

| Header        | Value                                                                                                                                                   |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `X-API-Key`   | Your publishable key (`pk_live_*` / `pk_test_*`).                                                                                                       |
| `X-Signature` | Hex HMAC-SHA256 of the base string, keyed with the secret key that pairs with your publishable key in the [dashboard](https://dashboard.encorekit.com). |
| `X-Timestamp` | Unix timestamp in seconds. Part of what you sign; the server rejects a skew over 5 minutes.                                                             |
| `X-Platform`  | Multi-app projects only, same rule as the [overview's Authentication section](/publishers/offers-api/overview#authentication).                          |

The base string is `timestamp.METHOD.path.query`, and two details cause most signature
failures:

* **The path you sign is not the URL you call.** Encore's gateway strips the `/encore`
  prefix before the API sees the request, so you call
  `/encore/publisher/sdk/v1/entitlements/server` but sign
  `/publisher/sdk/v1/entitlements/server`. Signing the public path is a silent `401`.
* **A GET signs a canonical query string, not a body.** Sort the parameters by key,
  URI-encode each key and value, and join with `&`. This endpoint has one parameter, so
  the canonical string is just `userId=...`, encoded.

```javascript theme={null}
// Backend (Node). Verify the entitlement server-to-server before you unlock.
import crypto from 'node:crypto';

const method = 'GET';
const path = '/publisher/sdk/v1/entitlements/server';   // the signed path: no /encore
const query = `userId=${encodeURIComponent(userId)}`;

const timestamp = Math.floor(Date.now() / 1000).toString();
const signature = crypto
  .createHmac('sha256', process.env.ENCORE_SECRET_KEY)
  .update(`${timestamp}.${method}.${path}.${query}`)
  .digest('hex');

const res = await fetch(`https://api.encorekit.com/encore${path}?${query}`, {
  method,
  headers: {
    'X-API-Key': process.env.ENCORE_PUBLISHABLE_KEY,   // pk_live_...
    'X-Signature': signature,
    'X-Timestamp': timestamp,
    'X-Platform': 'api',   // multi-app projects only
  },
});

if (!res.ok) return;   // 401 = bad signature, stale timestamp, or an unresolvable key

const { verified, provisional } = await res.json();
```

## Request

| Parameter | Type   | Required | Description                                                                                                                                       |
| --------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `userId`  | string | Yes      | The end-user identifier your integration uses everywhere else: the `userId` you send on offer requests, and the `userId` the webhook echoes back. |

## Response

```typescript theme={null}
interface EntitlementsResponse {
  success: boolean;
  provisional: EntitlementDetails;   // temporary, granted at claim time, awaiting verification
  verified: EntitlementDetails;      // permanent, earned through a verified completion
  all: EntitlementDetails;           // verified + provisional combined
  executionTimeMs: number;
}

interface EntitlementDetails {
  freeTrial?: { startedAt: string | null; expiresAt: string | null };
  discounts?: Array<{ value: number; unit: 'percent' | 'dollars'; expiresAt: string | null }>;
  credits?: { totalAmount: number; expiresAt: string | null };
}
```

Gate real access on **`verified`**. `provisional` exists for the immediate unlock a
client shows while the brand-side completion is still unverified; treat it as a UX
courtesy, never as proof. Timestamps are ISO 8601 and nullable.

## Status codes

| Code  | Meaning                                                                                                                                                         |
| ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `200` | Success. A user with no entitlements returns the same shape with empty detail objects, never a 404.                                                             |
| `401` | Signature does not verify, `X-Timestamp` skew over 5 minutes, or the key does not resolve. Recompute the signature against the signed path, not the public URL. |
| `5xx` | Encore-side failure. Retry with backoff.                                                                                                                        |

## Related

* [Offer Completed Webhook](/publishers/offers-api/reference/offer-completed-webhook): the push counterpart, delivered as each completion verifies.
* [Receive Completion Events](/publishers/offers-api/guides/receive-completion-events): wiring the webhook end to end.
* [The overview's Authentication section](/publishers/offers-api/overview#authentication): the key model and the `X-Platform` rule shared by every endpoint.
