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

# Subscription contracts

> Look up Appfox subscription contracts on Shopify by customer, email, or contract ID, then pause, resume, or cancel them through the REST API.

Find and manage subscription contracts that were created through Appfox.

## Contract identifiers

The API base URL is `https://subscriptions-app-new.getappfox.com`.

The `contractId` parameter can be either:

* The numeric Shopify contract ID (e.g., `123456789`)
* The full Shopify GID (e.g., `gid://shopify/SubscriptionContract/123456789`)

Official routes live under `/api/v1/subscription-contracts`. Do not use `/api/external/contracts`.

## Search contracts

```http theme={null}
GET /api/v1/subscription-contracts
```

All filters are optional and combine with each other.

| Parameter | Type | Description |
| - | - | - |
| `customerId` | string | Numeric Shopify customer ID or full Customer GID |
| `customerEmail` | string | Exact customer email address |
| `contractId` | string | Numeric Shopify contract ID or full SubscriptionContract GID |
| `status` | string | `ACTIVE`, `PAUSED`, `CANCELLED`, `EXPIRED`, or `FAILED` |
| `page` | integer | Page number. Defaults to `1` |
| `pageSize` | integer | Results per page from `1` to `100`. Defaults to `25` |

For example, find a customer's active contracts:

```bash theme={null}
curl "https://subscriptions-app-new.getappfox.com/api/v1/subscription-contracts?customerEmail=jane@example.com&status=ACTIVE" \
  -H "Authorization: Bearer afx_live_..."
```

```json Response (200 OK) theme={null}
{
  "data": [
    {
      "id": "gid://shopify/SubscriptionContract/123456789",
      "status": "ACTIVE",
      "customer": {
        "id": "gid://shopify/Customer/456",
        "firstName": "Jane",
        "lastName": "Doe",
        "email": "jane@example.com"
      },
      "billingPolicy": {
        "interval": "MONTH",
        "intervalCount": 1,
        "minCycles": 3,
        "maxCycles": null
      },
      "nextBillingDate": "2026-10-01T00:00:00.000Z",
      "scheduledResumeAt": null,
      "currencyCode": "EUR",
      "amount": 80,
      "createdAt": "2026-01-15T10:30:00.000Z"
    }
  ],
  "meta": {
    "page": 1,
    "pageSize": 25,
    "totalItems": 1,
    "totalPages": 1,
    "hasNextPage": false,
    "hasPreviousPage": false
  }
}
```

Email filter is an exact match. Search results come from Appfox's shop-scoped contract mirror. Get-by-id reads live Shopify data.

## Get a contract

```http theme={null}
GET /api/v1/subscription-contracts/{contractId}
```

This endpoint returns the current status, customer, billing policy, delivery
price, and product lines directly from Shopify. It also returns the scheduled
Appfox resume time when the contract is paused.

## Manage a contract

```http theme={null}
POST /api/v1/subscription-contracts/{contractId}/actions
```

## Pause a subscription

Pause a subscription for up to 60 days. The `resumeAt` field is **required** and must be a future ISO-8601 timestamp. The contract auto-resumes at that time via the billing cron.

This explicit `resumeAt` date always takes precedence over any shop-level auto-resume automation configured in Settings. The shop default only applies to portal and admin pauses that don't specify a resume date.

<CodeGroup>
  ```bash curl theme={null}
  curl https://subscriptions-app-new.getappfox.com/api/v1/subscription-contracts/123456789/actions \
    -X POST \
    -H "Authorization: Bearer afx_live_..." \
    -H "Content-Type: application/json" \
    -d '{
      "action": "pause",
      "resumeAt": "2026-10-15T09:00:00Z"
    }'
  ```

  ```json Request body theme={null}
  {
    "action": "pause",
    "resumeAt": "2026-10-15T09:00:00Z"
  }
  ```

  ```json Response (200 OK) theme={null}
  {
    "data": {
      "id": "gid://shopify/SubscriptionContract/123456789",
      "status": "PAUSED",
      "scheduledResumeAt": "2026-10-15T09:00:00.000Z"
    }
  }
  ```
</CodeGroup>

**Validation:**

* `resumeAt` is required on pause
* `resumeAt` must be a future timestamp
* Maximum pause duration is 60 days from the request time

## Resume a subscription

Resume a paused subscription immediately.

<CodeGroup>
  ```bash curl theme={null}
  curl https://subscriptions-app-new.getappfox.com/api/v1/subscription-contracts/123456789/actions \
    -X POST \
    -H "Authorization: Bearer afx_live_..." \
    -H "Content-Type: application/json" \
    -d '{"action": "resume"}'
  ```

  ```json Request body theme={null}
  {
    "action": "resume"
  }
  ```

  ```json Response (200 OK) theme={null}
  {
    "data": {
      "id": "gid://shopify/SubscriptionContract/123456789",
      "status": "ACTIVE",
      "scheduledResumeAt": null
    }
  }
  ```
</CodeGroup>

## Cancel a subscription

Permanently cancel a subscription. This action cannot be undone. Merchant and API cancels are not blocked by the plan-level minimum cycles setting. That setting only blocks customer-portal cancels.

<CodeGroup>
  ```bash curl theme={null}
  curl https://subscriptions-app-new.getappfox.com/api/v1/subscription-contracts/123456789/actions \
    -X POST \
    -H "Authorization: Bearer afx_live_..." \
    -H "Content-Type: application/json" \
    -d '{"action": "cancel"}'
  ```

  ```json Request body theme={null}
  {
    "action": "cancel"
  }
  ```

  ```json Response (200 OK) theme={null}
  {
    "data": {
      "id": "gid://shopify/SubscriptionContract/123456789",
      "status": "CANCELLED",
      "scheduledResumeAt": null
    }
  }
  ```
</CodeGroup>

## POS

Shopify POS can enroll customers in subscription selling plans directly at the register using the Appfox Subscriptions smart grid tile (requires POS 10.15+ and Shopify Payments). Staff add products to the cart, tap the **Subscriptions** tile, and select an available plan.

Custom POS extensions or third-party POS systems can use this API to look up, pause, resume, or cancel existing subscription contracts through server-to-server calls. Creating new subscriptions from a custom POS backend is not supported — use the native Shopify POS tile or online enrollment instead.

## Error responses

All errors use the following structure:

```json theme={null}
{
  "error": {
    "code": "error_code_here",
    "message": "Human-readable error message"
  }
}
```

### Status codes and error codes

| Status | Error Code | Description |
| - | - | - |
| `400` | `invalid_json` | Request body is not valid JSON |
| `400` | `invalid_contract_id` | Contract ID format is invalid |
| `400` | `invalid_customer_id` | Customer ID format is invalid |
| `400` | `invalid_customer_email` | Customer email is too long |
| `400` | `invalid_status` | Status filter is invalid |
| `400` | `invalid_pagination` | Page or page size is invalid |
| `401` | `unauthorized` | Invalid or revoked API key |
| `403` | `plan_required` | Shop's plan doesn't include API access (not returned while the app is free) |
| `404` | `contract_not_found` | Contract doesn't exist or doesn't belong to this shop |
| `422` | `invalid_action` | Action is not one of: pause, resume, cancel |
| `422` | `resume_at_required` | Pause action requires a `resumeAt` field |
| `422` | `invalid_resume_at` | `resumeAt` is not a valid ISO-8601 timestamp or is in the past |
| `422` | `suspension_too_long` | `resumeAt` is more than 60 days in the future |
| `422` | `shopify_rejected_action` | Shopify rejected the action (includes Shopify's error details) |
| `502` | `shopify_request_failed` | Shopify could not return live contract details |
| `500` | `internal_error` | An unexpected error occurred on the server |

### Example error responses

**Contract not found:**

```json theme={null}
{
  "error": {
    "code": "contract_not_found",
    "message": "No subscription contract with that ID belongs to this shop."
  }
}
```

**Invalid pause duration:**

```json theme={null}
{
  "error": {
    "code": "suspension_too_long",
    "message": "Subscriptions can only be paused for a maximum of 60 days."
  }
}
```

**Shopify rejected the action:**

```json theme={null}
{
  "error": {
    "code": "shopify_rejected_action",
    "message": "Shopify rejected the action: Contract is already paused."
  }
}
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.