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

# Lookup Subscriptions

> Check whether a user currently has an active subscription. Built for SaaS paywall integrations.

# Lookup Subscriptions

<div className="flex items-center gap-2 mb-4">
  <span className="px-2 py-1 bg-green-100 text-green-800 rounded text-sm font-mono font-bold">GET</span>
  <code>/subscriptions/lookup</code>
</div>

Returns the subscriptions a given end user has with your store, filtered to the ones that currently grant access (status `active` or `trialing`) by default.

This is the hot-path endpoint for **SaaS paywalls** — the typical integration asks "does this user have an active subscription?" on every protected request. The response includes a convenience `has_active` boolean so you can gate access with a single field check.

<Snippet file="rmz-plus-required.mdx" />

## Authentication

<Snippet file="auth-bearer-header.mdx" />

## Headers

| Header          | Value                   | Required    |
| --------------- | ----------------------- | ----------- |
| `Authorization` | `Bearer YOUR_API_TOKEN` | Yes         |
| `Accept`        | `application/json`      | Recommended |

## Rate Limit

This endpoint is throttled to **120 requests per minute** per API token — higher than the rest of the subscription endpoints because it is intended to be called on every paywalled request. If you expect higher volume, cache results briefly on your side (e.g. 60 seconds per user).

## Query Parameters

You must provide **at least one** of the following user identifiers:

* `external_customer_id`, **or**
* `email`, **or**
* `phone` (together with `country_code`).

If none of these are supplied the request is rejected with a `422`.

| Parameter              | Type             | Required    | Description                                                                                                                                                                                                                                                     |
| ---------------------- | ---------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `external_customer_id` | string (max 191) | Conditional | The merchant's own user identifier, stored on the subscription at checkout creation time. This is the fastest and most reliable lookup because it is indexed and isolated per store.                                                                            |
| `email`                | email (max 191)  | Conditional | Customer email. Matched exactly against the customer record.                                                                                                                                                                                                    |
| `country_code`         | string (max 5)   | Conditional | Country dial code without the `+` (e.g. `966`). A leading `+` is stripped automatically. Should be paired with `phone` to be useful.                                                                                                                            |
| `phone`                | string (max 15)  | Conditional | Customer phone number (local part only, no country code).                                                                                                                                                                                                       |
| `product_id`           | integer          | No          | Restrict results to a single product. Useful when you sell multiple subscription products and only want to check entitlement for one of them.                                                                                                                   |
| `include_inactive`     | boolean          | No          | Defaults to `false`. When `false`, only `active` and `trialing` subscriptions are returned (the "currently entitled" view). Set to `true` to also see `paused`, `past_due`, `canceled`, and `expired` rows, for example when building a billing history screen. |

<Note>
  The lookup is always scoped to your store — you will never see subscriptions that belong to another merchant, even if the same customer email exists across stores.
</Note>

## Example Requests

### Paywall check by external ID (most common)

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET "https://merchant-api.rmz.gg/shawarma/subscriptions/lookup?external_customer_id=usr_abc123" \
    -H "Authorization: Bearer YOUR_API_TOKEN"
  ```

  ```javascript JavaScript theme={null}
  const params = new URLSearchParams({
    external_customer_id: "usr_abc123"
  });

  const response = await fetch(
    `https://merchant-api.rmz.gg/shawarma/subscriptions/lookup?${params}`,
    { headers: { "Authorization": "Bearer YOUR_API_TOKEN" } }
  );
  const { data } = await response.json();

  if (data.has_active) {
    // grant access
  }
  ```

  ```python Python theme={null}
  import requests

  response = requests.get(
      "https://merchant-api.rmz.gg/shawarma/subscriptions/lookup",
      headers={"Authorization": "Bearer YOUR_API_TOKEN"},
      params={"external_customer_id": "usr_abc123"},
  )
  data = response.json()["data"]

  if data["has_active"]:
      grant_access()
  ```

  ```php PHP theme={null}
  $params = http_build_query(["external_customer_id" => "usr_abc123"]);
  $ch = curl_init("https://merchant-api.rmz.gg/shawarma/subscriptions/lookup?{$params}");
  curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer YOUR_API_TOKEN"]);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  $response = json_decode(curl_exec($ch), true);

  if ($response["data"]["has_active"]) {
      // grant access
  }
  ```
</CodeGroup>

### Lookup by email, restricted to a specific product

```bash theme={null}
curl -X GET "https://merchant-api.rmz.gg/shawarma/subscriptions/lookup?email=ahmed@example.com&product_id=102" \
  -H "Authorization: Bearer YOUR_API_TOKEN"
```

### Lookup by phone, including historical subscriptions

```bash theme={null}
curl -X GET "https://merchant-api.rmz.gg/shawarma/subscriptions/lookup?country_code=966&phone=501234567&include_inactive=1" \
  -H "Authorization: Bearer YOUR_API_TOKEN"
```

## Success Response

```json theme={null}
{
  "message": null,
  "data": {
    "count": 1,
    "has_active": true,
    "subscriptions": [
      {
        "id": 501,
        "status": "active",
        "external_customer_id": "usr_abc123",
        "starts_at": "2024-01-01",
        "ends_at": "2024-12-31",
        "start_date": "2024-01-01",
        "end_date": "2024-12-31",
        "current_period_start": "2024-01-01T00:00:00.000000Z",
        "current_period_end": "2024-12-31T00:00:00.000000Z",
        "trial_ends_at": null,
        "cancel_at_period_end": false,
        "canceled_at": null,
        "duration": "annually",
        "order_id": 78901,
        "auto_renew": true,
        "price": {
          "amount": 199.99,
          "formatted": "١٩٩٫٩٩ ر.س",
          "currency": "SAR"
        },
        "product": {
          "id": 102,
          "name": "Pro Plan",
          "slug": "pro-plan",
          "type": "subscription"
        },
        "variant": {
          "id": 15,
          "duration": "annually",
          "duration_text": "سنه",
          "price": 199.99
        },
        "metadata": {
          "external_user_id": "usr_abc123",
          "plan": "pro"
        },
        "features": null,
        "is_active": true,
        "is_expired": false,
        "days_remaining": 276,
        "created_at": "2024-01-01T00:00:00.000000Z",
        "updated_at": "2024-01-01T00:00:00.000000Z"
      }
    ]
  },
  "api": "rmz.shawarma",
  "timestamp": 1699999999
}
```

## Response Fields

| Field           | Type    | Description                                                                                                                                                                                                                                       |
| --------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `count`         | integer | Number of subscriptions returned. The endpoint caps results at **50** per call.                                                                                                                                                                   |
| `has_active`    | boolean | `true` if at least one of the returned rows is `active` or `trialing`. Use this as your paywall gate.                                                                                                                                             |
| `subscriptions` | array   | Array of subscription objects. Each object has the same shape as [Get Subscription](./get-subscription), **without** the `payment_method`, `scheduled_variant`, or `customer` fields (only `product` and `variant` relations are preloaded here). |

### Empty result

When no subscription matches, the endpoint still returns `200 OK` with an empty array — there is no `404`:

```json theme={null}
{
  "message": null,
  "data": {
    "count": 0,
    "has_active": false,
    "subscriptions": []
  },
  "api": "rmz.shawarma",
  "timestamp": 1699999999
}
```

## Common Integration Pattern

A minimal SaaS paywall middleware:

```javascript theme={null}
async function hasActiveSubscription(userId) {
  const res = await fetch(
    `https://merchant-api.rmz.gg/shawarma/subscriptions/lookup?external_customer_id=${encodeURIComponent(userId)}&product_id=102`,
    { headers: { Authorization: `Bearer ${process.env.RMZ_API_TOKEN}` } }
  );

  if (!res.ok) {
    // Fail closed or fail open depending on your risk tolerance.
    // Most paywalls fail closed on 5xx and fail open on network errors.
    return false;
  }

  const { data } = await res.json();
  return data.has_active;
}
```

<Tip>
  Pass `external_customer_id` when you create the checkout session (see [Create Checkout Session](./create-checkout-session)). That ID is stored on the subscription itself, so the lookup is a direct indexed read instead of a `customers` table join.
</Tip>

## Error Responses

| Code  | Description                                                                                                                                                         |
| ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `401` | Unauthorized — invalid or missing API token.                                                                                                                        |
| `403` | Subscription billing is not enabled for the store (RMZ Plus required).                                                                                              |
| `422` | Validation error. Returned when no identifier is provided, or when a parameter exceeds its length limit / fails the type check (e.g. `email` is not a valid email). |
| `429` | Rate limit exceeded (120 requests per minute per token).                                                                                                            |

### Example `422` — no identifier supplied

```json theme={null}
{
  "message": "At least one of external_customer_id, email, or phone is required",
  "data": null,
  "api": "rmz.shawarma",
  "timestamp": 1699999999
}
```

### Example `422` — invalid parameter

```json theme={null}
{
  "message": "The given data was invalid.",
  "errors": {
    "email": ["The email must be a valid email address."]
  }
}
```
