# ThriveCart > ThriveCart is a hosted shopping cart, checkout and course platform for creators, coaches and > digital-product sellers, operated by ThriveCart LLC. Its public REST API at > https://thrivecart.com/api/external manages products, order bumps, upsells, downsells, pricing > options, transactions, customers, subscriptions, affiliates, Learn students and event > subscriptions. ## Getting started - [ThriveCart Developers portal](https://developers.thrivecart.com/): app registration, credentials, documentation - [Getting started](https://developers.thrivecart.com/documentation/): API key vs OAuth, rate limits, PHP SDK - [Authentication via API key](https://developers.thrivecart.com/documentation/intro/authentication-via-api-key/): create a key under Settings > API & webhooks > API tokens - [Authentication via OAuth](https://developers.thrivecart.com/documentation/intro/authentication-via-oauth/): authorization-code flow for apps acting on another account - [API reference](https://apidocs.thrivecart.com/): Postman-powered interactive reference with code generation ## The contract - Base URL: `https://thrivecart.com/api/external` - Auth: `Authorization: Bearer ` on every call - OAuth 2.0 authorization-code endpoints: `https://thrivecart.com/authorization/new`, `https://thrivecart.com/authorization/token` - Rate limit: **60 requests per minute, per connected account**. ThriveCart states it does not raise limits pre-emptively. - Error envelope: `{"error": "", "error_description": ""}` — plain JSON, not RFC 9457 `application/problem+json` - ThriveCart publishes **no OpenAPI**. API Evangelist derives one from ThriveCart's own Postman collection: `openapi/thrivecart-api-openapi.yml` (33 operations) - ThriveCart publishes **no OAuth scope reference**. Consent grants account-wide access. ## Operations by resource - **Account** — `GET /ping` (validate a token, read account name, id, version, URL, user) - **Products** — `GET /products`, `GET /products/{product_id}`, `GET /products/{product_id}/pricing_options` - **Bumps** — `GET /bumps`, `GET /bumps/{bump_id}`, `GET /bumps/{bump_id}/pricing_options` - **Upsells** — `GET /upsells`, `GET /upsells/{upsell_id}`, `GET /upsells/{upsell_id}/pricing_options` - **Downsells** — `GET /downsells`, `GET /downsells/{downsell_id}`, `GET /downsells/{downsell_id}/pricing_options` - **Transactions** — `GET /transactions?page&perPage&query&transactionType¤cy` (perPage max 100) - **Customers** — `POST /customer` (read by email), `POST /customerEmailUpdate` - **Subscriptions** — `POST /refund`, `POST /cancelSubscription`, `POST /pauseSubscription`, `POST /resumeSubscription` - **Affiliates** — `GET /affiliates` (perPage max 25), `POST /affiliate`, `POST /affiliates`, and `POST /affiliates/{affiliate_id}/{favorite|unfavorite|register|approve|reject|custom_commissions|delete}` - **Learn** — `POST /students` - **Event subscriptions** — `POST /subscribe`, `POST /unsubscribe` ## Events Two distinct event surfaces exist and they do not share event names. 1. **Event Subscription API** (JSON, targeted) — `POST /subscribe` with `{"event": "...", "target_url": "...", "trigger_fields": {...}}`. 21 event keys: `order_created`, `order_payment_product|bump|upsell|downsell`, `order_rebill`, `order_rebill_failed|completed|cancelled`, `order_refund_product|bump|upsell|downsell`, `subscription_paused`, `subscription_resumed`, `cart_abandoned`, `affiliate_approved`, `affiliate_rejected`, `affiliate_commission_earned|payout|refund`. Use `*` for all events. 2. **Account-wide webhooks** (form-encoded, configured in the UI under Settings > API & Webhooks). Different names: `order.success`, `order.refund`, `cart.abandoned`, `order.subscription_payment`, `order.subscription_cancelled|paused|resumed`, `order.rebill_failed`, `affiliate.commission_earned|payout|refund`. Up to 5 destinations by default. Delivery idempotency: every event-subscription delivery carries a `webhook_id` UUID that is **stable across retries** — use it as the idempotency key. `event_id` is retained for backward compatibility but is tied to the transaction record and may be absent on some event types. Authenticity: webhook payloads carry a `thrivecart_secret` field (the account's secret word) plus `thrivecart_account`. There is no HMAC signature header. ## SDKs - PHP (official): `composer require thrivecart/php-api` — https://github.com/thrivecart/php-api. Latest release **1.0.11, published 2022-01-19**; unchanged for over four years while the API has continued to gain fields. - Examples (official): https://github.com/thrivecart/api-demo - No official JavaScript, Python, Go, Ruby or .NET client. The `thrivecart` npm package is third-party (magloft). - No first-party CLI. ## Operations - Status page: https://thrivecart.statuspage.io/ (Atlassian Statuspage, component-level, with an incident history and a JSON API at `/api/v2/summary.json`) - Product updates / changelog: https://thrivecart.com/blog/category/product-updates/ (dated posts, RSS at `.../feed/`) - Roadmap: https://thrivecart.com/resources/roadmap/ - Support: https://support.thrivecart.com/ - Security contact: security@thrivecart.com (RFC 9116 security.txt at https://thrivecart.com/.well-known/security.txt) ## Testing ThriveCart has a per-product **test mode** rather than a separate sandbox host or a key prefix. Set a product to test mode in its Options tab; the funnel, automation rules, membership fulfilment and webhooks all fire, but no live payment is taken. In the API, mode is signalled by `mode_int` (`1` = test, `2` = live) and can be used as an event-subscription trigger field. Test cards come from the connected processor, e.g. Stripe's `4242 4242 4242 4242`. ## Known gaps - No provider-published OpenAPI, AsyncAPI or JSON Schema - No `/.well-known/oauth-authorization-server`; every unmatched `/.well-known/*` path returns a 200 HTML SPA shell - No A2A agent card, no first-party MCP server (as of 2026-08-12) - No OAuth scope model — consent is account-wide - No documented `Retry-After` or `RateLimit-*` response headers on the 60/min limit - No documented request-id / trace header - No deprecation or sunset policy, no versioned API path (the SDK reports `API_VERSION 1.0.0`)