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

# Incentives

## Worked incentives samples

The endpoint list below documents every operation's parameters; this
overlay walks an incentive the way a promotions API actually uses it:
**browse the provider catalog → issue an incentive → list the issued
ledger → handle a redeem or void**. Success envelopes are `{ data, meta }`,
error envelopes `{ error, meta }` — see [How to read a worked
sample](/guides/using-orbit-samples). The standalone promotions engine is
state-projected over the tenant `cdp_events` ledger (the same append-only
store the loyalty layer uses), so issuance is migration-free and a read
always converges.

Every response carries `meta.request_id`. Quote the request id when you
report a credit that landed on the wrong ledger — a support trace needs
both the `incentive_id` and the request id.

### 1. Browse the provider catalog

`GET /api/v1/incentives/providers` returns every incentive fulfillment
provider the engine can call: the internal Orbit credit ledger, the
discount-code minter, external gift-card networks (Tremendous/Tango
style), and any custom fulfillment. Use this to drive the
reward-source picker or to decide which `provider_id` to override on the
issue call.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET "https://api.orbit.devotel.io/api/v1/incentives/providers" \
    -H "X-API-Key: dv_test_sk_YOUR_KEY"
  ```

  ```typescript Node.js theme={null}
  const res = await fetch(
    "https://api.orbit.devotel.io/api/v1/incentives/providers",
    {
      headers: { "X-API-Key": process.env.ORBIT_API_KEY! },
    },
  );
  console.log(await res.json());
  ```
</CodeGroup>

```json 200 theme={null}
{
  "data": {
    "providers": [
      { "id": "orbit-credit", "kind": "internal", "currencies": ["*"] },
      { "id": "discount-code", "kind": "internal", "currencies": ["*"] },
      { "id": "tremendous", "kind": "external", "currencies": ["USD"] }
    ]
  },
  "meta": {
    "request_id": "req_inc_providers",
    "timestamp": "2026-08-26T12:00:00.000Z"
  }
}
```

### 2. Issue an incentive

`POST /api/v1/incentives/issue` creates one incentive — a credit, a
discount code, a gift card, or a custom payout — and records the
outcome on the tenant `cdp_events` ledger. The body is the
`issueBodySchema`: required `reward_type` (one of `credit`,
`discount_code`, `gift_card`, `custom`), optional `amount`,
`provider_id` (when omitted the engine picks a default per reward type),
and the fulfillment payload the provider returns. A write of this kind
is gated to owner, admin, or developer roles — the same guard the
referral reward-payout surface uses.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.orbit.devotel.io/api/v1/incentives/issue" \
    -H "X-API-Key: dv_test_sk_YOUR_KEY" \
    -H "Content-Type: application/json" \
    -d '{
    "reward_type": "discount_code",
    "amount": 25,
    "metadata": { "campaign": "aug-winback" }
  }'
  ```

  ```typescript Node.js theme={null}
  const res = await fetch(
    "https://api.orbit.devotel.io/api/v1/incentives/issue",
    {
      method: "POST",
      headers: {
        "X-API-Key": process.env.ORBIT_API_KEY!,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        reward_type: "discount_code",
        amount: 25,
        metadata: { campaign: "aug-winback" },
      }),
    },
  );
  console.log(await res.json());
  ```
</CodeGroup>

```json 200 theme={null}
{
  "data": {
    "id": "inc_8fb2d1b7c00c4ec9",
    "reward_type": "discount_code",
    "amount": 25,
    "provider_id": "discount-code",
    "status": "issued",
    "code": "ORBIT-WINBACK-4KQJ"
  },
  "meta": {
    "request_id": "req_inc_issue",
    "timestamp": "2026-08-26T12:01:00.000Z"
  }
}
```

### 3. List the issued ledger

`GET /api/v1/incentives/issued` returns every incentive the tenant has
issued, newest first, with its reward type, provider, amount, and status.
Use this to drive a win-back report or the org's own incentive audit
before you call redeem or void.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET "https://api.orbit.devotel.io/api/v1/incentives/issued" \
    -H "X-API-Key: dv_test_sk_YOUR_KEY"
  ```

  ```typescript Node.js theme={null}
  const res = await fetch(
    "https://api.orbit.devotel.io/api/v1/incentives/issued",
    {
      headers: { "X-API-Key": process.env.ORBIT_API_KEY! },
    },
  );
  console.log(await res.json());
  ```
</CodeGroup>

```json 200 theme={null}
{
  "data": {
    "issued": [
      {
        "id": "inc_8fb2d1b7c00c4ec9",
        "reward_type": "discount_code",
        "status": "issued",
        "amount": 25
      }
    ]
  },
  "meta": {
    "request_id": "req_inc_ledger",
    "timestamp": "2026-08-26T12:02:00.000Z"
  }
}
```

### 4. Errors

Errors follow the `{ error, meta }` envelope. Two failures every
promotions integrator hits:

**403 — role too low for a write.**

```json 403 theme={null}
{
  "error": {
    "code": "INSUFFICIENT_PERMISSIONS",
    "message": "This action requires one of: owner, admin, developer",
    "status": 403
  },
  "meta": {
    "request_id": "req_inc_err",
    "timestamp": "2026-08-26T12:03:00.000Z"
  }
}
```

**422 — unknown provider or wrong reward semantics.** An override that
names a provider the catalog cannot resolve, or an amount that picks a
negative debit:

```json 422 theme={null}
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Unknown provider id; check /incentives/providers for allowed values.",
    "status": 422
  },
  "meta": {
    "request_id": "req_inc_err2",
    "timestamp": "2026-08-26T12:03:30.000Z"
  }
}
```
