ecom9 API Documentation
Access transactions, product catalogs, subscription state, and tier data for your connected accounts. Every request is authenticated with your Payment Gateway keys. See Subscription Event Handling: Design Patterns and Best Practices for lifecycle event guidance.
Getting Your API Key
- Sign in to the ecom9 dashboard.
- Open Payment Gateways.
- Select your active sandbox or live gateway.
- Copy the API Key and signing key ID from the gateway details and store them securely.
Integration Overview
account_ref_id is the primary identity key between ecom9 and your software. Use it to map customers, orgs, or workspaces and keep the same value across all API calls.
- Required on most endpoints: pass
account_ref_id on requests that read or write billing state.
- Stable and internal: choose an immutable ID from your system (user/org/workspace) to avoid remapping later.
- Consistent across systems: we echo
account_ref_id back in responses and webhook payloads.
Sandbox and production API keys are available in the dashboard under Payment Gateways, letting you pick the right environment per deployment.
Prefer using an SDK? Jump to SDK Examples for language-specific snippets that call each endpoint.
Authentication
All endpoints require your Payment Gateway API key. Include it with each request using one of the supported headers.
Option 1: Authorization header (recommended)
Authorization: Bearer YOUR_API_KEY
Option 2: X-API-Key header
X-API-Key: YOUR_API_KEY
Base URL: https://app.ecom9.com/api/
Test with curl
curl -s https://app.ecom9.com/api/tiers/ \
-H "Authorization: Bearer YOUR_API_KEY"
Endpoints
Base URL: https://app.ecom9.com/api/
Get Transactions by Account Reference ID
Retrieve the full transaction history for a specific account reference.
Request URL
GET https://app.ecom9.com/api/transactions/ACC123456789/?page=1&page_size=20
Parameters
account_ref_id — path parameter (required)
page — query parameter (optional, default 1)
page_size — query parameter (optional, default 20, max 100)
status — query parameter (optional: pending, completed, failed, cancelled)
Example response
{
"count": 5,
"next": null,
"previous": null,
"results": [
{
"transaction_uuid": "123e4567-e89b-12d3-a456-426614174000",
"account_ref_id": "ACC123456789",
"status": "completed",
"amount": "29.99",
"currency": "usd",
"customer_email": "customer@example.com",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:35:00Z"
}
]
}
Check Account Status
Return the live subscription state for an account reference, optionally filtered to a specific subscription + tier combination.
Request URL
GET https://app.ecom9.com/api/account/F8xQ2PnL7vKd93sH/status/?subscription=studio-suite&tier=pro
Parameters
account_ref_id — path parameter (required)
subscription — query parameter (optional subscription label, required whenever tier is provided)
tier — query parameter (optional tier label, only evaluated when paired with subscription)
Example response
{
"account_ref_id": "TWY4k2ZQPp19s8Hd",
"is_active": true,
"customer_email": "orchid.ops@examplemail.com",
"created_at": "2024-03-02T14:11:27Z",
"updated_at": "2024-03-02T14:11:27Z",
"subscription_tier": {
"label_name": "pro",
"name": "Aurora Growth",
"description": "45k send limit and co-marketing boosts",
"price": "32.00",
"billing_cycle": "monthly",
"features": "Deliverability audits, Campaign playbooks, Concierge migrations",
"is_popular": true,
"product_manage_id": "P6ZmR29aTyQb"
}
}
Example response (multiple subscriptions)
{
"account_ref_id": "TWY4k2ZQPp19s8Hd",
"customer_email": "orchid.ops@examplemail.com",
"subscriptions": [
{
"is_active": false,
"created_at": "2023-12-18T09:54:12Z",
"updated_at": "2024-01-01T00:00:00Z",
"subscription_tier": {
"label_name": "starter",
"name": "Pulse Lite",
"description": "5k send trial bundle",
"price": "0.00",
"billing_cycle": "monthly",
"features": "API sandbox, Limited automation",
"is_popular": false,
"product_manage_id": "V8bLw4cNq0Xs"
}
},
{
"is_active": true,
"created_at": "2024-03-02T14:11:27Z",
"updated_at": "2024-03-02T14:11:27Z",
"subscription_tier": {
"label_name": "pro",
"name": "Aurora Growth",
"description": "45k send limit and co-marketing boosts",
"price": "32.00",
"billing_cycle": "monthly",
"features": "Deliverability audits, Campaign playbooks, Concierge migrations",
"is_popular": true,
"product_manage_id": "P6ZmR29aTyQb"
}
}
],
"total_subscriptions": 2
}
List Products
Return the products for the authenticated user’s profile and payment gateway, ordered by title.
Request URL
GET https://app.ecom9.com/api/products/
Response fields
manage_id — product’s unique manager ID
label_name — label name (for SaaS/subscription use)
type — product type (single or subscription)
Example response
{
"results": [
{ "manage_id": "abc12xyz", "label_name": "my-product", "type": "single" },
{ "manage_id": "def34uvw", "label_name": "pro-plan", "type": "subscription" }
],
"count": 2
}
Create Invoice
Create a Stripe-hosted invoice for a product and return the hosted payment URL. Optionally email the invoice to the customer.
Request URL
POST https://app.ecom9.com/api/invoices/create/
Sample payload (return invoice URL)
{
"product_manage_id": "abc12xyz",
"amount": 49.99,
"customer_email": "customer@example.com"
}
Sample payload (email the customer)
{
"product_manage_id": "abc12xyz",
"amount": 49.99,
"customer_email": "customer@example.com",
"currency": "usd",
"description": "One-time setup fee",
"send_to_customer": true,
"return_url": "https://app.example.com/thank-you"
}
Example response
{
"hosted_invoice_url": "https://invoice.stripe.com/i/acct_xxx/...",
"stripe_invoice_id": "in_1ABCdef...",
"email_sent": true,
"return_url": "https://app.example.com/thank-you"
}
Get Available Subscription Tiers
List every tier configured for your account so you can display pricing or validate selection on the client side.
Request URL
GET https://app.ecom9.com/api/tiers/?page=1&page_size=20
Example response
{
"count": 3,
"results": [
{
"label_name": "basic",
"name": "Basic Plan",
"description": "Essential features for getting started",
"price": "9.99",
"billing_cycle": "monthly",
"features": "Basic features, Email support",
"is_popular": false,
"product_manage_id": "ABC123DEF456"
}
]
}
Resolve Public URL
Fetch the customer-facing link for a SaaS product or tier before appending your signed ?u= token. The resolver only returns URLs for catalog entries tagged saas_only.
Mandatory requirements
account_ref_id is required on every request and must match the account that owns the subscription.
- When resolving by
subscription_name + tier_name, include email so we can match the subscriber.
- Non-SaaS catalog items respond with
404 Not Found. Confirm the access mode before resolving.
Request URL
GET https://app.ecom9.com/api/resolve/public-url/?product_name=onboarding&account_ref_id=demo-account-123
Parameters
account_ref_id — query parameter (required)
product_name — query parameter (optional, mutually exclusive with subscription_name + tier_name)
subscription_name — query parameter (optional, requires tier_name)
tier_name — query parameter (optional, requires subscription_name)
email — query parameter (required when using subscription_name + tier_name)
Tip: Use product_name for one-time product sales. Use subscription_name + tier_name for subscriptions with saas_mode enabled.
Subscription + tier parameters
Use when you only know the catalog names. Required query params: subscription_name, tier_name, account_ref_id, email.
curl -X GET "https://app.ecom9.com/api/resolve/public-url/?subscription_name=pro-plan&tier_name=pro&account_ref_id=demo-account-123&email=buyer%40example.com" \
-H "Authorization: Bearer YOUR_API_KEY_HERE"
Single product parameters
Required query params: product_name, account_ref_id.
curl -X GET "https://app.ecom9.com/api/resolve/public-url/?product_name=onboarding&account_ref_id=demo-account-123" \
-H "Authorization: Bearer YOUR_API_KEY_HERE"
Example response
{
"public_url": "https://app.ecom9.com/p/offer123?u=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImlzcyI6ImRlbW8tYWNjb3VudC0xMjMifQ.eyJhY2NvdW50X3JlZl9pZCI6ImRlbW8tYWNjb3VudC0xMjMiLCJ0aWVyIjoicHJvIiwiaWF0IjoxNzAwMDAwMDB9._fake-signature-abc123"
}
Fix Subscription
Return a hosted payment URL for the invoice that failed and put a subscription into a past-due or incomplete state.
Request URL
GET https://app.ecom9.com/api/fix-subscription/?account_ref_id=IAS0I48ZMYEF&subscription_name=pro-plan&tier_name=pro
Parameters
account_ref_id — query parameter (required)
subscription_name — query parameter (required, requires tier_name)
tier_name — query parameter (required, requires subscription_name)
Example response
{
"url": "https://app.ecom9.com/pay/in_1PLsXjKz8C7Demo"
}
If there is no past-due invoice or the subscription is already active, the response body is empty.
Check Entitlements
Confirm whether an account has access to a product entitlement. Auth uses the Payment Gateway API key and scopes to the gateway owner.
Request URL
GET https://app.ecom9.com/api/entitlements/check/?account_ref_id=org_123&product_code=pro-api&entitlement_code=api_access
Query parameters
account_ref_id — downstream identity (required)
product_code — stable product code (required)
entitlement_code — entitlement code (required)
Example response
{
"account_ref_id": "org_123",
"product_code": "pro-api",
"entitlement_code": "api_access",
"has_access": true
}
Get Prepaid Credit Balance
Return remaining prepaid credits for an account, product, and meter. If no balance row exists, the balance is 0.
Request URL
GET https://app.ecom9.com/api/usage/credits/balance/?account_ref_id=org_123&product_code=ai-images&meter_code=credits
Query parameters
account_ref_id — downstream identity (required)
product_code — product code (required)
meter_code — meter code (required, e.g. credits, api_calls)
Example response
{
"account_ref_id": "org_123",
"product_code": "ai-images",
"meter_code": "credits",
"balance": 42
}
Debit Prepaid Credits (Authorization)
Use for products with usage_model="prepaid_credits". Call this before performing an action to authorize and debit credits.
Request URL
POST https://app.ecom9.com/api/usage/credits/debit/
Request body
{
"account_ref_id": "org_123",
"product_code": "ai-images",
"meter_code": "credits",
"quantity": 5,
"event_key": "imggen_987",
"metadata": {
"feature": "image_generation",
"request_id": "req_123"
}
}
Fields
account_ref_id — required
product_code — required; must use prepaid_credits
meter_code — required; meter must belong to the product
quantity — required; positive integer to debit
event_key — required; unique idempotency key per debit attempt
metadata — optional JSON blob for your own tracking
Success response
{
"approved": true,
"debited": 5,
"remaining_balance": 42
}
Insufficient credits
{
"approved": false,
"error_code": "insufficient_credits",
"remaining_balance": 2
}
Configuration errors
{
"approved": false,
"error_code": "invalid_usage_model",
"remaining_balance": 0
}
Idempotency: event_key maps to an internal ledger ref_key. Repeating the call with the same key returns the same effective result and does not double-debit.
Record Usage Events (Postpaid / Metered)
Use for postpaid or metered usage models: metered_postpaid, included_overage, tiered_usage, volume_usage. Call after an action to record immutable usage.
Request URL
POST https://app.ecom9.com/api/usage/events/
Request body
{
"account_ref_id": "org_123",
"product_code": "api-access",
"meter_code": "api_calls",
"quantity": 1,
"event_key": "req_abc_123",
"metadata": {
"path": "/v1/generate",
"method": "POST",
"request_id": "req_abc_123"
}
}
Fields
account_ref_id — required
product_code — required; must use a postpaid usage model
meter_code — required; meter must belong to the product
quantity — required; positive integer units to record
event_key — required; unique idempotency key per usage event
metadata — optional JSON blob
Success response
{
"accepted": true,
"recorded_quantity": 1,
"idempotent": false
}
Idempotent retry
{
"accepted": true,
"recorded_quantity": 1,
"idempotent": true
}
Configuration errors
{
"accepted": false,
"error_code": "invalid_usage_model"
}
Get Usage Summary
Summarize usage for an account/product/meter over an optional time window. Use for reporting or billing runs.
Request URL
GET https://app.ecom9.com/api/usage/summary/?account_ref_id=org_123&product_code=api-access&meter_code=api_calls&start=2026-03-01T00:00:00Z&end=2026-03-31T23:59:59Z
Query parameters
account_ref_id — required
product_code — required
meter_code — required
start — optional ISO-8601 timestamp (inclusive)
end — optional ISO-8601 timestamp (inclusive)
Example response
{
"account_ref_id": "org_123",
"product_code": "api-access",
"meter_code": "api_calls",
"total_usage": 1250
}
If no events exist, total_usage is 0.
Webhooks
We deliver webhook events so your product can react instantly without duplicating Stripe plumbing.
- Payload consistency: Every webhook includes the original
account_ref_id and a unique transaction_id so you can correlate events or detect retries.
- Subscription coverage: We process Stripe subscription lifecycle events (payment succeeded, failed, cancellation, cancel at period end, incomplete) for you.
- Simple consumption: Call the status endpoint after receiving a webhook if you need confirmation. The response always resolves to a yes/no signal for access control.
Sample webhook payload
{
"event": "subscription-created",
"transaction_id": "123e4567-e89b-12d3-a456-426614174000",
"account_ref_id": "ACC123456789",
"subscription_id": "sub_xxxx",
"is_active": true,
"label_name": "pro", // tier's label name
"timestamp": "2024-01-15T10:35:00Z"
}
# Your webhook endpoint should respond with 200 OK after processing
HTTP/1.1 200 OK
Content-Type: application/json
{"received": true}
Error Responses
401 Unauthorized
{
"detail": "Invalid API key"
}
404 Not Found
{
"error": "Account not found or invalid subscription tier"
}
400 Bad Request
{
"field_name": ["Error message describing the issue"]
}
Rate Limiting
If you exceed your plan’s rate limit, responses include 429 Too Many Requests. Inspect the Retry-After header before retrying.
Data Privacy
- Only data tied to your Payment Gateway account is returned.
- Customer email addresses appear for identification only.
- Sensitive payment identifiers (Stripe IDs) are never exposed.
- All timestamps use UTC (ISO 8601) format.
SDK Examples
The official SDKs wrap the REST endpoints so you can keep signing, pagination, and retries consistent across services. Use the tabs to switch languages.
Get Transactions
from ecom9_sdk import Ecom9Client
client = Ecom9Client(api_key="YOUR_API_KEY")
transactions = client.transactions.list(
account_ref_id="ACC123456789",
page=1,
status="completed"
)
for entry in transactions.results:
print(entry["transaction_uuid"], entry["status"])
<?php
use Ecom9\Sdk\Client;
$client = new Client([
'api_key' => getenv('ECOM9_KEY'),
]);
$transactions = $client->transactions()->list([
'account_ref_id' => 'ACC123456789',
'page' => 1,
]);
foreach ($transactions['results'] as $tx) {
echo $tx['transaction_uuid'] . ' ' . $tx['status'] . PHP_EOL;
}
import { Ecom9Client } from "@ecom9/sdk";
const client = new Ecom9Client({ apiKey: process.env.ECOM9_KEY });
const transactions = await client.transactions.list({
accountRefId: "ACC123456789",
page: 1,
});
transactions.results.forEach(tx => {
console.log(`${tx.transaction_uuid} ${tx.status}`);
});
import { useTransactions } from "@ecom9/react";
export function TransactionsList({ accountRefId }) {
const { data, isLoading, error } = useTransactions({
accountRefId,
page: 1,
});
if (isLoading) return <p>Loading…</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<ul>
{data.results.map(tx => (
<li key={tx.transaction_uuid}>
{tx.status} — {tx.amount}
</li>
))}
</ul>
);
}
Check Account Status
status = client.accounts.status(
account_ref_id="ACC123456789",
tier="pro"
)
print(status["is_active"], status["subscription_tier"]["label_name"])
$status = $client->accounts()->status([
'account_ref_id' => 'ACC123456789',
'tier' => 'pro',
]);
echo $status['is_active'] ? 'active' : 'inactive';
const status = await client.accounts.status({
accountRefId: "ACC123456789",
tier: "pro",
});
console.log(status.is_active);
import { useAccountStatus } from "@ecom9/react";
export function AccountStatus({ accountRefId }) {
const { data } = useAccountStatus({ accountRefId, tier: "pro" });
if (!data) return null;
return (
<span>
{data.is_active ? "Active" : "Inactive"} — {data.subscription_tier.label_name}
</span>
);
}
Get Subscription Tiers
tiers = client.tiers.list()
print([tier["label_name"] for tier in tiers["results"]])
$tiers = $client->tiers()->list();
return array_column($tiers['results'], 'label_name');
const tiers = await client.tiers.list();
console.log(tiers.results.map(tier => tier.label_name));
import { useTiers } from "@ecom9/react";
export function TierSelect() {
const { data, isLoading } = useTiers();
if (isLoading) return <option>Loading…</option>;
return (
<select>
{data.results.map(tier => (
<option key={tier.label_name} value={tier.label_name}>
{tier.name}
</option>
))}
</select>
);
}
Resolve Public URL
Use the SDK helper to wrap /api/resolve/public-url/. Refer to the endpoint documentation above for required parameters and SaaS-only constraints.
public_url = client.links.resolve_public_url(
product_name="onboarding",
account_ref_id="demo-account-123",
)
print(public_url["public_url"])
$url = $client->links()->resolvePublicUrl([
'subscription_name' => 'pro-plan',
'tier_name' => 'pro',
'account_ref_id' => 'demo-account-123',
'email' => 'buyer@example.com',
]);
echo $url['public_url'];
const url = await client.links.resolvePublicUrl({
productName: "onboarding",
accountRefId: "demo-account-123",
});
console.log(url.public_url);
import { useResolvePublicUrl } from "@ecom9/react";
export function CheckoutLink({ productName, accountRefId }) {
const { data } = useResolvePublicUrl({ productName, accountRefId });
if (!data) return null;
return (
<a href={data.public_url} target="_blank" rel="noreferrer">
View checkout
</a>
);
}
Support
Need a new endpoint or payload tweak? Reach the team through your dashboard chat or email support, and we’ll scope it with you.