# post\_\_credits\_packs\_buy

`POST /credits/packs/buy`

*Buy a Credit Pack*

Initiates a Stripe Checkout session to purchase a credit pack for the authenticated user's organization. The request body must include the pack ID (from `GET /credits/packs`). The organization must have a paid subscription (Starter or above); Free tier cannot buy packs. A per-pack-type limit per billing cycle applies (e.g. 3 of each pack per cycle). On success, the response includes a checkout URL and session ID; the user is redirected to Stripe to complete payment. Credits are added to the organization when the payment succeeds (via webhook).

#### TypeScript Client Library

```typescript
public buyCreditsPack = async (body: BuyCreditsPackRequest): Promise<BuyCreditsPackResponse> => {
  return this.makeRequest<BuyCreditsPackResponse>('credits/packs/buy', 'POST', body);
};
```

#### Code Samples

{% tabs %}
{% tab title="Shell" %}

```shell
curl -X POST https://backend.flashback.tech/credits/packs/buy \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer {access-token}' \
  -d '{"packId": "pack-1"}'
```

{% endtab %}

{% tab title="HTTP" %}

```http
POST https://backend.flashback.tech/credits/packs/buy HTTP/1.1
Host: backend.flashback.tech
Accept: application/json
Content-Type: application/json
Authorization: Bearer {access-token}

{"packId": "pack-1"}
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const headers = {
  'Accept': 'application/json',
  'Content-Type': 'application/json',
  'Authorization': 'Bearer {access-token}'
};
const body = { packId: 'pack-1' };

fetch('https://backend.flashback.tech/credits/packs/buy', {
  method: 'POST',
  headers,
  body: JSON.stringify(body)
})
  .then(res => res.json())
  .then(data => {
    if (data.success && data.checkoutUrl) window.location.href = data.checkoutUrl;
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

headers = {
  'Accept': 'application/json',
  'Content-Type': 'application/json',
  'Authorization': 'Bearer {access-token}'
}
body = { 'packId': 'pack-1' }

r = requests.post('https://backend.flashback.tech/credits/packs/buy', headers=headers, json=body)
print(r.json())
```

{% endtab %}
{% endtabs %}

> Example responses

> 200 Response

```json
{
  "success": true,
  "checkoutUrl": "https://checkout.stripe.com/c/pay/cs_xxx",
  "sessionId": "cs_xxx"
}
```

> 400 Response (credits not enabled)

```json
{
  "success": false,
  "error_code": "CREDITS_NOT_ENABLED",
  "message": "Credits system is not enabled"
}
```

> 403 Response (not authorized / Free tier)

```json
{
  "success": false,
  "error_code": "SUBSCRIPTION_REQUIRED",
  "message": "Credit pack purchases require Starter subscription or above"
}
```

> 403 Response (no billing permission)

```json
{
  "success": false,
  "error_code": "NOT_AUTHORIZED",
  "message": "User does not have permission to purchase credit packs"
}
```

> 404 Response (pack not found)

```json
{
  "success": false,
  "error_code": "PACK_NOT_FOUND",
  "message": "Credit pack not found or not active"
}
```

> 429 Response (pack limit reached)

```json
{
  "success": false,
  "error_code": "PACK_LIMIT_REACHED",
  "message": "Maximum 3 purchases of this pack type per billing cycle",
  "nextAvailableAt": "2026-02-15T00:00:00.000Z"
}
```

> 500 Response

```json
{
  "success": false,
  "error_code": "INTERNAL_ERROR",
  "message": "Failed to process credit pack purchase"
}
```

#### Request Body

| Name     | Type   | Required | Description                              |
| -------- | ------ | -------- | ---------------------------------------- |
| » packId | string | true     | Credit pack ID (from GET /credits/packs) |

#### Responses

| Status | Meaning               | Description                                         | Schema |
| ------ | --------------------- | --------------------------------------------------- | ------ |
| 200    | OK                    | Checkout URL and session ID                         | Inline |
| 400    | Bad Request           | Credits not enabled, no org, or pack not configured | Inline |
| 403    | Forbidden             | No billing permission or Free tier                  | Inline |
| 404    | Not Found             | User, org, or pack not found                        | Inline |
| 429    | Too Many Requests     | Pack limit reached for this billing cycle           | Inline |
| 500    | Internal Server Error | Server error                                        | Inline |

#### Response Schema (200)

| Name              | Type               | Required | Description                                                     |
| ----------------- | ------------------ | -------- | --------------------------------------------------------------- |
| » success         | boolean            | false    | Whether the request succeeded                                   |
| » checkoutUrl     | string \| null     | false    | Stripe Checkout URL; redirect user here to pay                  |
| » sessionId       | string             | false    | Stripe Checkout Session ID                                      |
| » error\_code     | string             | false    | Set on error                                                    |
| » message         | string             | false    | Error or status message                                         |
| » nextAvailableAt | string (date-time) | false    | When limit reached: next date a pack of this type can be bought |

#### Security

* **BearerAuth**: Bearer token required.
* **Organization**: Purchase is for the user's organization.
* **Role / subscription**: User must have billing permission; organization must have a paid subscription (Starter or above).

#### Notes

* Redirect the user to `checkoutUrl` to complete payment. After payment, Stripe redirects back to the app (success/cancel URLs are set by the backend).
* Credits are added to the organization when the Stripe webhook receives the successful payment; no need to poll from the client.
* Limit is per pack type per billing cycle (e.g. 3× Pack 1, 3× Pack 2, 3× Pack 3 in the same cycle). Billing cycle is based on "day X" from the org's subscription start date.
* If the pack has no Stripe price configured, the backend returns 400 with `STRIPE_NOT_CONFIGURED`.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.flashback.tech/support-reference/platform-api-reference/credits/post__credits_packs_buy.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
