Codes customers enter at checkout on payment links that allow them (allowDiscountCodes): a percentage or a fixed amount off the subtotal, with an optional limit on uses and an expiry. Codes are stored upper case and are unique per business. Add ?code=FALL15 to a link's url to apply one when checkout opens.
| Topic | Detail |
|---|---|
| Endpoints | `POST /api/v1/discount-codes`, `GET /api/v1/discount-codes`, `GET /api/v1/discount-codes/{id}`, `POST /api/v1/discount-codes/{id}/deactivate` |
| Key permissions | discount_codes:write, discount_codes:read |
| Samples | curl; Node.js 18+ with the built-in fetch (in an ES module); the Node SDK (preview: not on npm yet); Python with requests. |
| Every request can also answer | 401 UNAUTHORIZED, 401 API_KEY_EXPIRED, 403 NOT_ENROLLED, 403 PERMISSION_DENIED, 403 IP_NOT_ALLOWED, 429 RATE_LIMITED, 500 INTERNAL_ERROR. See Error codes. |
The discount code object
{
"object": "discount_code",
"livemode": true,
"id": "dc_311",
"code": "FALL15",
"percentOffBps": 1500,
"amountOffCents": null,
"maxRedemptions": 100,
"redemptions": 12,
"expiresAt": "2026-11-30T23:59:59.000Z",
"status": "active",
"active": true,
"createdAt": "2026-10-01T08:00:00.000Z",
"archivedAt": null
}| Field | Type | Description |
|---|---|---|
object | string | The type of object. Always discount_code. |
livemode | boolean | False for test-mode data (an ak_test_ key); true for live data. |
id | string | dc_… |
code | string | What customers type, upper case. |
percentOffBps | integer or null | Percent off in basis points (1500 is 15%). |
amountOffCents | integer or null | Amount off. Integer cents. |
maxRedemptions | integer or null | Most uses. |
redemptions | integer | Uses so far. |
expiresAt | string (date-time) or null | Expires. |
status | string | active, expired (past expiresAt), used_up (reached maxRedemptions) or archived (deactivated). |
active | boolean | Accepted at checkout now: not expired, not used up and not deactivated. |
createdAt | string (date-time) | Created. |
archivedAt | string (date-time) or null | Deactivated. |
Endpoints
Create a discount code
/api/v1/discount-codesdiscount_codes:writeSupports Idempotency-Key.A percentage or fixed amount off. Unique per business: 409 CODE_TAKEN otherwise. Codes made here apply to payment links that allow them. To use a code on subscription plans too, turn on Use on subscriptions for it in the dashboard (see Subscription discounts).
Headers
Idempotency-Keystring- Recommended. A unique key such as a UUID: a retry with the same key within 24 hours gets the first response back instead of acting twice. See Idempotency.
Body
codestringrequired- 3–40 letters, numbers, dashes or underscores; stored upper case.
percentOffBpsinteger or null- Percent off in basis points: 1500 is 15%. From 1 to 10,000.
amountOffCentsinteger or null- Amount off, in cents. From 1 ($0.01) to 1,000,000 ($10,000.00).
maxRedemptionsinteger or null- Most uses. From 1 to 1,000,000.
expiresAtstring (date-time) or null- When it stops working: in the future, within five years.
curl -X POST https://anytime-capital.com/api/v1/discount-codes \
-H "Authorization: Bearer $ANYTIME_API_KEY" \
-H "Idempotency-Key: discount-fall15" \
-H "Content-Type: application/json" \
-d '{
"code": "FALL15",
"percentOffBps": 1500,
"maxRedemptions": 100
}'const res = await fetch("https://anytime-capital.com/api/v1/discount-codes", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ANYTIME_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": "discount-fall15",
},
body: JSON.stringify({ code: "FALL15", percentOffBps: 1500, maxRedemptions: 100 }),
});
const discountCode = await res.json();
if (!res.ok) throw new Error(`${discountCode.error.code}: ${discountCode.error.message}`);import { AnytimeCapital } from "@anytimecapital/node";
const client = new AnytimeCapital({ apiKey: process.env.ANYTIME_API_KEY! });
const discountCode = await client.discountCodes.create(
{ code: "FALL15", percentOffBps: 1500, maxRedemptions: 100 },
{ idempotencyKey: "discount-fall15" },
);import os
import requests
res = requests.post(
"https://anytime-capital.com/api/v1/discount-codes",
headers={
"Authorization": f"Bearer {os.environ['ANYTIME_API_KEY']}",
"Idempotency-Key": "discount-fall15",
},
json={"code": "FALL15", "percentOffBps": 1500, "maxRedemptions": 100},
timeout=30,
)
discountCode = res.json()
if not res.ok:
raise RuntimeError(f"{discountCode['error']['code']}: {discountCode['error']['message']}"){
"object": "discount_code",
"livemode": true,
"id": "dc_311",
"code": "FALL15",
"percentOffBps": 1500,
"amountOffCents": null,
"maxRedemptions": 100,
"redemptions": 0,
"expiresAt": null,
"status": "active",
"active": true,
"createdAt": "2026-10-01T08:00:00.000Z",
"archivedAt": null
}Errors
- 400
INVALID_JSON - The body isn't valid JSON, or couldn't be read. An empty body reads as
{}. SendContent-Type: application/jsonand a JSON object. - 400
INVALID_INPUT - A field, filter,
limitorcursoris missing or invalid. The message starts with the field's name, as inamountCents: …, or readsInvalid cursor.Fix the named field. Don't retry the same request unchanged. - 400
INVALID_IDEMPOTENCY_KEY - The
Idempotency-Keyheader is empty, longer than 255 characters, or has characters other than printable ASCII. Use a UUID. See Idempotency. - 409
CODE_TAKEN - The business already has a discount code with this code. Pick another code, or use the existing one.
- 422
IDEMPOTENCY_KEY_REUSED - The
Idempotency-Keywas used in the last 24 hours with a different body or endpoint. Use a new key for a new request; reuse a key only to retry the same request.
List discount codes
/api/v1/discount-codesdiscount_codes:readDiscount codes, newest first.
Query
activebooleantruefor codes not deactivated (they may still have expired or run out: seestatus),falsefor deactivated ones.limitinteger- How many objects per page, 1–100. Default 25. See Pagination.
cursorstring- The previous page's
nextCursor, for the page after it.
curl "https://anytime-capital.com/api/v1/discount-codes?active=true" \
-H "Authorization: Bearer $ANYTIME_API_KEY"const res = await fetch("https://anytime-capital.com/api/v1/discount-codes?active=true", {
headers: { Authorization: `Bearer ${process.env.ANYTIME_API_KEY}` },
});
const page = await res.json();
if (!res.ok) throw new Error(`${page.error.code}: ${page.error.message}`);import { AnytimeCapital } from "@anytimecapital/node";
const client = new AnytimeCapital({ apiKey: process.env.ANYTIME_API_KEY! });
const page = await client.discountCodes.list({ active: true });import os
import requests
res = requests.get(
"https://anytime-capital.com/api/v1/discount-codes",
headers={"Authorization": f"Bearer {os.environ['ANYTIME_API_KEY']}"},
params={"active": "true"},
timeout=30,
)
page = res.json()
if not res.ok:
raise RuntimeError(f"{page['error']['code']}: {page['error']['message']}"){
"object": "list",
"data": [
{
"object": "discount_code",
"livemode": true,
"id": "dc_311",
"code": "FALL15",
"percentOffBps": 1500,
"amountOffCents": null,
"maxRedemptions": 100,
"redemptions": 12,
"expiresAt": "2026-11-30T23:59:59.000Z",
"status": "active",
"active": true,
"createdAt": "2026-10-01T08:00:00.000Z",
"archivedAt": null
}
],
"hasMore": false,
"nextCursor": null
}Errors
- 400
INVALID_INPUT - A field, filter,
limitorcursoris missing or invalid. The message starts with the field's name, as inamountCents: …, or readsInvalid cursor.Fix the named field. Don't retry the same request unchanged.
Get a discount code
/api/v1/discount-codes/{id}discount_codes:readOne discount code.
Path
idstringrequireddc_…
curl https://anytime-capital.com/api/v1/discount-codes/dc_311 \
-H "Authorization: Bearer $ANYTIME_API_KEY"const res = await fetch("https://anytime-capital.com/api/v1/discount-codes/dc_311", {
headers: { Authorization: `Bearer ${process.env.ANYTIME_API_KEY}` },
});
const discountCode = await res.json();
if (!res.ok) throw new Error(`${discountCode.error.code}: ${discountCode.error.message}`);import { AnytimeCapital } from "@anytimecapital/node";
const client = new AnytimeCapital({ apiKey: process.env.ANYTIME_API_KEY! });
const discountCode = await client.discountCodes.retrieve("dc_311");import os
import requests
res = requests.get(
"https://anytime-capital.com/api/v1/discount-codes/dc_311",
headers={"Authorization": f"Bearer {os.environ['ANYTIME_API_KEY']}"},
timeout=30,
)
discountCode = res.json()
if not res.ok:
raise RuntimeError(f"{discountCode['error']['code']}: {discountCode['error']['message']}"){
"object": "discount_code",
"livemode": true,
"id": "dc_311",
"code": "FALL15",
"percentOffBps": 1500,
"amountOffCents": null,
"maxRedemptions": 100,
"redemptions": 12,
"expiresAt": "2026-11-30T23:59:59.000Z",
"status": "active",
"active": true,
"createdAt": "2026-10-01T08:00:00.000Z",
"archivedAt": null
}Errors
- 404
NOT_FOUND - No such object on your business (an id from another business reads the same), or no such endpoint under
/api/v1, which still answers in JSON. Check the id and the path. Ids are case-sensitive.
Deactivate a discount code
/api/v1/discount-codes/{id}/deactivatediscount_codes:writeSupports Idempotency-Key.Stop it being accepted at checkout. Safe to repeat. Payments that used it keep it; bring it back from Discounts in the dashboard.
Path
idstringrequireddc_…
Headers
Idempotency-Keystring- Recommended. A unique key such as a UUID: a retry with the same key within 24 hours gets the first response back instead of acting twice. See Idempotency.
curl -X POST https://anytime-capital.com/api/v1/discount-codes/dc_311/deactivate \
-H "Authorization: Bearer $ANYTIME_API_KEY" \
-H "Idempotency-Key: discount-fall15-off"const res = await fetch("https://anytime-capital.com/api/v1/discount-codes/dc_311/deactivate", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ANYTIME_API_KEY}`,
"Idempotency-Key": "discount-fall15-off",
},
});
const discountCode = await res.json();
if (!res.ok) throw new Error(`${discountCode.error.code}: ${discountCode.error.message}`);import { AnytimeCapital } from "@anytimecapital/node";
const client = new AnytimeCapital({ apiKey: process.env.ANYTIME_API_KEY! });
const discountCode = await client.discountCodes.deactivate(
"dc_311",
{ idempotencyKey: "discount-fall15-off" },
);import os
import requests
res = requests.post(
"https://anytime-capital.com/api/v1/discount-codes/dc_311/deactivate",
headers={
"Authorization": f"Bearer {os.environ['ANYTIME_API_KEY']}",
"Idempotency-Key": "discount-fall15-off",
},
timeout=30,
)
discountCode = res.json()
if not res.ok:
raise RuntimeError(f"{discountCode['error']['code']}: {discountCode['error']['message']}"){
"object": "discount_code",
"livemode": true,
"id": "dc_311",
"code": "FALL15",
"percentOffBps": 1500,
"amountOffCents": null,
"maxRedemptions": 100,
"redemptions": 12,
"expiresAt": "2026-11-30T23:59:59.000Z",
"status": "archived",
"active": false,
"createdAt": "2026-10-01T08:00:00.000Z",
"archivedAt": "2026-10-24T12:00:00.000Z"
}Errors
- 400
INVALID_IDEMPOTENCY_KEY - The
Idempotency-Keyheader is empty, longer than 255 characters, or has characters other than printable ASCII. Use a UUID. See Idempotency. - 404
NOT_FOUND - No such object on your business (an id from another business reads the same), or no such endpoint under
/api/v1, which still answers in JSON. Check the id and the path. Ids are case-sensitive. - 422
IDEMPOTENCY_KEY_REUSED - The
Idempotency-Keywas used in the last 24 hours with a different body or endpoint. Use a new key for a new request; reuse a key only to retry the same request.