API Fundamentals
Base URL, path naming, money/timestamp formats, pagination, sorting, the response format, and rate limits — read this before wiring anything up; every other page assumes you already know these rules.
Base URL, path naming & versioning
Every endpoint lives under /api on your environment's URL (e.g. $REBATE_PAY_BASE_URL/api/Product). Paths follow /api/{Resource}[/{resourceId}][/{Action}]: resources are singular PascalCase nouns (Product, PaymentLink, WebhookEndpoint), a single item adds {resourceId}, and non-CRUD actions come last (/api/Subscription/{subscriptionId}/Cancel, /api/Payment/{paymentId}/Reconcile). Paths match case-insensitively, but the docs always use PascalCase.
| HTTP method | Used for | Example |
|---|---|---|
GET | List or retrieve | GET /api/Product, GET /api/Product/{productId} |
POST | Create a resource or run an action | POST /api/Product, POST /api/Refund, POST /api/PaymentLink/{paymentLinkId}/Archive |
PUT | Update a resource (send only the fields to change) | PUT /api/Product/{productId} |
DELETE | Delete a resource | DELETE /api/PaymentMethod/{paymentMethodId} |
Developer API paths have no prefix; ReBate Pay's own console APIs use Admin* (platform console), Merchant* (merchant console) and Member* (consumer wallet) prefixes and sign in with JWTs (see Authentication). Full spec: GET /api/OpenApi?audience=developer. The API version is a date string (e.g. 2026-08-30), returned in the x-rebatepay-version response header.
environment parameter, resolves to sandbox; a Production endpoint isn't available yet.Object ID format
Every resource ID is a prefix plus a random string. The prefix tells you the resource type at a glance and gives your code something cheap to sanity-check:
| Prefix | Resource |
|---|---|
prod_ | Product |
price_ | Price |
cus_ | Customer |
sub_ | Subscription |
pay_ | Payment |
re_ | Refund |
plink_ | Payment Link |
evt_ | Webhook Event |
wda_ | Webhook Delivery Attempt |
Money: integers, no decimal places
amount here is not a minor unit like cents. TWD has no decimal places, so amount: 200 means NT$200, not NT$2. If you're porting the "multiply by 100" convention from another API, you'll overcharge by 100x — adjust it.Time: ISO 8601, UTC
Every timestamp field is an ISO 8601 string in UTC (e.g. 2026-08-30T00:00:00.000Z). The dashboard converts to Taipei time for display; raw API fields are always UTC — don't assume Asia/Taipei.
Pagination: page / pageSize
Every list endpoint takes the same page-number parameters, and the response carries the total count and page count:
| Parameter | Meaning |
|---|---|
page | Page number, starting at 1 (default 1) |
pageSize | Items per page, 1–200 (default 20) |
Response page / pageSize | The page and page size actually used |
Response totalCount / totalPages | Matching items and pages; page < totalPages means there's another page |
curl -sS "$BASE/api/Product?page=1&pageSize=20" -H "Authorization: Bearer $API_KEY"
# Next page:
curl -sS "$BASE/api/Product?page=2&pageSize=20" -H "Authorization: Bearer $API_KEY"Filtering & sorting
| Parameter | Meaning |
|---|---|
orderBy | Field to sort by (a response field name such as createdAt or amount), at most 50 characters; newest first when omitted |
orderDirection | ASC or DESC (default DESC) |
createdFrom / createdTo | Creation-time range, ISO 8601 date-time |
status | Endpoint-specific status as a PascalCase enum value (e.g. Active, Succeeded) |
keyword (or q) | Free-text search, at most 200 characters |
Fields & enum values
JSON fields are always camelCase (customerId, successUrl). Status, type and interval values are PascalCase: payment status Pending, Succeeded, Failed, Unknown; subscription status Trialing, Active, PastDue, Canceled; price type OneTime, Recurring; interval Month, Year. Event types keep their dotted form (payment.succeeded).
Response format
On success the body is in data; on failure it's in error and the HTTP status reflects the kind of error. Every response carries an x-request-id header — include it when contacting support:
{
"data": { "id": "plink_...", "status": "Active", "url": "https://.../pay/..." }
}error.nextAction is a human-readable suggestion (currently zh-TW); error.code is the stable string your code should branch on. Full error code reference: Troubleshooting / Error Catalog.
Rate limits
Each API key defaults to 120 requests per 60-second fixed window (not a sliding window). Every response carries:
| Header | Meaning |
|---|---|
x-ratelimit-limit | Total quota for this window |
x-ratelimit-remaining | Requests left in this window |
x-ratelimit-reset | When the quota resets (Unix timestamp, seconds) |
Exceeding the limit returns 429 RATE_LIMITED with a Retry-After header (seconds) — wait exactly that long before retrying instead of guessing a fixed backoff.
Retries & Idempotency-Key
GETs are always safe to retry. Any financial mutation (payment, refund, checkout) requires an Idempotency-Key header — omit it and you get 400 IDEMPOTENCY_KEY_REQUIRED. Resending the same key with the same idempotency key returns the original result (with x-idempotent-replay: true) instead of executing it again. Full details: Refunds & Idempotency.
ReBate Pay Docs