API Reference
Base Configuration
| Property | Value |
|---|---|
| Production Base URL | https://api.payparse.ca/v1 |
| Authentication | Authorization: Bearer <API_KEY> |
| Content-Type | application/json |
| Idempotency | Redis-backed, 24-hour TTL per key |
API Key Formats
| Prefix | Type | Mode | Access Level |
|---|---|---|---|
pp_pub_* | Publishable | Live | Client-side checkout creation only |
pp_pub_test_* | Publishable | Test | Client-side test checkout only |
pp_live_* | Secret | Live | Full server-side API access (livemode: true) |
pp_live_test_* | Secret | Test | Full server-side API access (livemode: false) |
pp_rk_* | Restricted | Live | Scope-limited server-side access |
Secret keys created in the dashboard include checkout.write, payments.read, and events.read by default. Test secret keys never read or mutate live sessions/events.
1. Create Checkout Session
POST /v1/checkout/sessions
Initializes a new checkout session and returns a redirect URL or embedded client secret.
Required Headers
| Header | Type | Required | Description |
|---|---|---|---|
Authorization | String | Yes | Bearer pp_live_*, Bearer pp_live_test_*, or Bearer pp_rk_* |
Idempotency-Key | String | Yes | Unique UUID to prevent duplicate session creation. Cached for 24 hours. |
Content-Type | String | Yes | application/json |
Required Scopes
checkout.write
Test keys (pp_live_test_*) create sessions with livemode: false. Live keys create livemode: true.
Request Body
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
amount | Integer | Yes | Positive integer, value in cents (e.g., 2500 = $25.00 CAD) | Transaction amount in CAD cents |
currency | String | Yes | Must be exactly "cad" (lowercase) | Currency code — only CAD is supported |
reference | String | Yes | Non-empty, max 255 characters, unique per merchant | Your platform's tracking ID (invoice, Odoo S00021, order id). Not shown to the payer. |
success_url | String | Yes | Fully qualified URL (https://...) | Redirect destination on successful payment |
cancel_url | String | Yes | Fully qualified URL (https://...) | Redirect destination on payment cancellation or expiry |
metadata | Object | No | Arbitrary key-value pairs | Custom metadata attached to the session and forwarded to webhooks |
Response Codes
| Code | Meaning | Body |
|---|---|---|
201 | Session created successfully | Session object (see below) |
200 | Idempotent cache hit | Original session response + header X-Cache-Lookup: HIT |
400 | Validation error | { "error": "Validation failed: ..." } |
401 | Unauthorized | { "error": "Invalid or missing API key" } |
403 | Forbidden (scope) | { "error": "Insufficient scope: checkout.write required" } |
409 | Reference conflict | { "error": "Reference already exists" } |
Response Body (201 Created)
{
"id": "cuid_generated_session_id",
"checkout_url": "https://checkout.payparse.ca/sessions/cuid_generated_session_id",
"client_secret": "cs_secret_token_for_embedded_frames",
"status": "PENDING",
"livemode": true,
"reference": "INV-2026-0847",
"memo": "PP-7K2M9Q",
"expires_at": "2026-07-15T17:30:00.000Z"
}
Field Descriptions
| Field | Type | Description |
|---|---|---|
id | String | Unique checkout session identifier (CUID) |
checkout_url | String | Fully qualified hosted checkout page URL for redirect flow |
client_secret | String | Token for the embedded iframe checkout mode — never expose client-side |
status | Enum | Always "PENDING" on creation |
livemode | Boolean | true for live keys, false for test keys |
reference | String | Echo of the merchant tracking ID you sent |
memo | String | Payparse-issued Interac message (PP-XXXXXX). Hosted checkout shows this; ingest matches it. |
expires_at | ISO 8601 | Absolute expiration timestamp — session expires 30 minutes after creation |
cURL Example
curl -X POST https://api.payparse.ca/v1/checkout/sessions \
-H "Authorization: Bearer pp_live_your_secret_key" \
-H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
-H "Content-Type: application/json" \
-d '{
"amount": 25000,
"currency": "cad",
"reference": "INV-2026-0847",
"success_url": "https://yoursite.com/payment/success?session_id={SESSION_ID}",
"cancel_url": "https://yoursite.com/payment/cancelled"
}'
Node.js Example
const response = await fetch('https://api.payparse.ca/v1/checkout/sessions', {
method: 'POST',
headers: {
'Authorization': 'Bearer pp_live_your_secret_key',
'Idempotency-Key': crypto.randomUUID(),
'Content-Type': 'application/json',
},
body: JSON.stringify({
amount: 25000,
currency: 'cad',
reference: 'INV-2026-0847',
success_url: 'https://yoursite.com/payment/success?session_id={SESSION_ID}',
cancel_url: 'https://yoursite.com/payment/cancelled',
}),
});
const session = await response.json();
// session.id, session.checkout_url, session.client_secret
2. Retrieve Checkout Session
GET /v1/checkout/sessions/:id
Returns the full state of an existing checkout session.
Required Headers
| Header | Type | Required | Description |
|---|---|---|---|
Authorization | String | Yes | Bearer pp_live_* or Bearer pp_rk_* |
Content-Type | String | Yes | application/json |
Required Scopes
checkout.write
Sessions are isolated by merchant and livemode: a test key cannot retrieve a live session (and vice versa).
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | String | Yes | The checkout session identifier returned during creation |
Response Codes
| Code | Meaning | Body |
|---|---|---|
200 | Session found | Full CheckoutSession object |
401 | Unauthorized | { "error": "Invalid or missing API key" } |
403 | Forbidden (scope) | { "error": "Insufficient scope: checkout.write required" } |
404 | Not found | { "error": "Checkout session not found" } |
Response Body (200 OK)
{
"id": "cuid_generated_session_id",
"amount": 25000,
"currency": "CAD",
"reference": "INV-2026-0847",
"memo": "PP-7K2M9Q",
"status": "PENDING",
"livemode": true,
"client_secret": "cs_secret_token_for_embedded_frames",
"success_url": "https://yoursite.com/payment/success?session_id={SESSION_ID}",
"cancel_url": "https://yoursite.com/payment/cancelled",
"metadata": null,
"expires_at": "2026-07-15T17:30:00.000Z",
"created_at": "2026-07-15T17:00:00.000Z"
}
Field Descriptions
| Field | Type | Description |
|---|---|---|
id | String | Unique checkout session identifier |
amount | Integer | Transaction amount in CAD cents |
currency | String | Always "CAD" |
reference | String | Your platform tracking ID |
memo | String | Payparse-issued Interac memo shown to the payer |
status | Enum | Current session state |
livemode | Boolean | Whether the session was created with a live key |
client_secret | String | Embedded checkout token |
success_url | String | Merchant redirect on success |
cancel_url | String | Merchant redirect on cancel |
metadata | Object | null | Custom metadata from creation |
expires_at | ISO 8601 | Absolute expiration timestamp |
created_at | ISO 8601 | Session creation timestamp |
Session Status Values
| Status | Description |
|---|---|
PENDING | Session active, awaiting customer payment |
COMPLETED | Customer completed payment successfully |
EXPIRED | Session expired (30-minute window elapsed) |
cURL Example
curl -X GET https://api.payparse.ca/v1/checkout/sessions/cuid_generated_session_id \
-H "Authorization: Bearer pp_live_your_secret_key" \
-H "Content-Type: application/json"
3. List Transactions
GET /v1/payments
Returns a filtered list of payment transactions. Use this to query the transactional ledger.
Required Headers
| Header | Type | Required | Description |
|---|---|---|---|
Authorization | String | Yes | Bearer pp_live_* or Bearer pp_rk_* |
Content-Type | String | Yes | application/json |
Required Scopes
payments.read
Query Parameters
| Parameter | Type | Required | Default | Max | Description |
|---|---|---|---|---|---|
reference | String | No | — | — | Partial match against interac_ref, payment comment, or checkout session reference |
status | String | No | — | — | Filter by payment status (see enum values below) |
limit | Integer | No | 10 | 50 | Maximum number of results to return (values outside 1–50 → 400) |
Results are scoped to the authenticated merchant and the API key’s livemode.
Status Filter Values
| Value | Description |
|---|---|
PENDING | Payment received, awaiting confirmation |
CONFIRMED | Payment verified and confirmed |
FAILED | Payment failed or rejected |
REFUNDED | Payment refunded to sender |
REQUIRES_MANUAL_REVIEW | Auto-verification failed — requires admin review |
Response Codes
| Code | Meaning | Body |
|---|---|---|
200 | Success | Array of Transaction objects |
400 | Bad request | { "error": "Invalid query parameters" } |
401 | Unauthorized | { "error": "Invalid or missing API key" } |
403 | Forbidden (scope) | { "error": "Insufficient scope: payments.read required" } |
Response Body (200 OK)
[
{
"id": "cuid_transaction_id",
"interac_ref": "PP-A1B2C3",
"amount": 25000,
"sender_name": "Jane Smith",
"comment": "Invoice INV-2026-0847",
"status": "CONFIRMED",
"verification_method": "AUTO_EMAIL",
"created_at": "2026-07-15T17:15:00.000Z"
}
]
Field Descriptions
| Field | Type | Description |
|---|---|---|
id | String | Unique transaction identifier |
interac_ref | String | Reference code parsed from the Interac e-Transfer payload (e.g., PP-A1B2C3) |
amount | Integer | Transaction amount in CAD cents |
sender_name | String | Name of the person who sent the e-Transfer |
sender_email | String | null | Sender email address (if available) |
comment | String | null | Payment comment/message from the Interac transfer |
status | Enum | Current payment status |
verification_method | Enum | How the payment was verified |
created_at | ISO 8601 | Transaction creation timestamp |
Verification Method Values
| Method | Description |
|---|---|
AUTO_EMAIL | Automatically confirmed via email parsing and comment matching |
MANUAL_ADMIN | Manually verified by a merchant administrator |
VO_PAY | Verified via VoPay PSP integration (future) |
cURL Examples
List all transactions:
curl -X GET "https://api.payparse.ca/v1/payments?limit=20" \
-H "Authorization: Bearer pp_live_your_secret_key" \
-H "Content-Type: application/json"
Filter by status:
curl -X GET "https://api.payparse.ca/v1/payments?status=REQUIRES_MANUAL_REVIEW&limit=50" \
-H "Authorization: Bearer pp_live_your_secret_key" \
-H "Content-Type: application/json"
Filter by reference:
curl -X GET "https://api.payparse.ca/v1/payments?reference=INV-2026-0847" \
-H "Authorization: Bearer pp_live_your_secret_key" \
-H "Content-Type: application/json"
Node.js Example
const params = new URLSearchParams({
status: 'PENDING',
limit: '25',
});
const response = await fetch(`https://api.payparse.ca/v1/payments?${params}`, {
headers: {
'Authorization': 'Bearer pp_live_your_secret_key',
},
});
const transactions = await response.json();
// transactions[].interac_ref, transactions[].status, etc.
4. List Events
GET /v1/events
Returns immutable platform events for the authenticated merchant (same livemode as the API key). Delivery attempts are available on the detail endpoint.
Required Scopes
events.read
Query Parameters
| Parameter | Type | Required | Default | Max | Description |
|---|---|---|---|---|---|
type | String | No | — | — | Filter by event type (e.g. payment.confirmed) |
limit | Integer | No | 10 | 50 | Maximum number of results (1–50) |
Response Body (200 OK)
[
{
"id": "evt_unique_event_id",
"type": "payment.confirmed",
"livemode": true,
"data": {
"reference": "INV-2026-0847",
"amount": 25000,
"status": "CONFIRMED"
},
"created_at": "2026-07-15T17:15:00.000Z"
}
]
5. Retrieve Event
GET /v1/events/:id
Returns one event plus webhook delivery attempts (status, HTTP code, latency, errors). Events are insert-only — there is no update or delete API.
Required Scopes
events.read
Response Body (200 OK)
{
"id": "evt_unique_event_id",
"type": "payment.confirmed",
"livemode": true,
"data": {
"reference": "INV-2026-0847",
"amount": 25000,
"status": "CONFIRMED"
},
"created_at": "2026-07-15T17:15:00.000Z",
"deliveries": [
{
"webhook_id": "wh_…",
"url": "https://example.com/hooks",
"delivery_status": "SUCCESS",
"http_status": 200,
"attempt": 1,
"retry_count": 0,
"latency_ms": 120,
"error_message": null
}
]
}
6. Sandbox Simulate Payment
POST /v1/sandbox/simulate-payment
Simulates Interac outcomes for test-mode checkout sessions only. Requires a test secret key (pp_live_test_*). Live keys receive 403. See Sandbox / test mode.
Required Scopes
checkout.write
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
sessionId | String | One of sessionId / reference | Test checkout session id |
reference | String | One of sessionId / reference | Case-insensitive checkout reference |
outcome | String | Yes | confirmed, failed, or requires_manual_review |
Ownership, expiry, and terminal-state checks apply. Simulations always enqueue webhooks with livemode: false.
Refunds (dashboard)
Completed Interac deposits are not reversed in place. Merchants request a full refund from the dashboard (/dashboard/transactions/:id). Admins approve, mark outbound Interac as sent, and complete with evidence.
| Stage | Notes |
|---|---|
REQUESTED | Merchant request; test-mode auto-approves |
APPROVED / REJECTED | Admin decision (live) |
SENT | Outbound Interac marked sent |
COMPLETED | Payment status → REFUNDED; emits exactly one payment.refunded event |
FAILED / CANCELLED | Terminal non-success |
Live completion requires an outbound Interac reference and evidence note. Only one open or completed refund is allowed per payment. Idempotent completion retries do not re-emit payment.refunded.
Error Response Format
All error responses follow a consistent structure:
{
"error": "Human-readable error message describing the failure"
}
Common Error Codes
| Code | Description |
|---|---|
400 | Request body validation failed — check field types and constraints |
401 | API key is missing, expired, or invalid |
403 | API key does not have the required scope for this endpoint |
404 | Resource not found — verify the identifier |
409 | Conflict — the reference already exists (idempotency violation) |
429 | Rate limit exceeded — back off and retry after the Retry-After header |
500 | Internal server error — contact [email protected] with the request ID |
Rate Limits
API requests may be throttled under abuse. When rate limited, expect HTTP 429. Exact per-key quotas are configured at the edge; do not rely on fixed client-side QPS assumptions for correctness — use idempotency keys for writes.
Webhook Events
Outbound webhook payloads are delivered to your registered endpoints and signed with X-Payparse-Signature (HMAC-SHA256). See Webhook Integrations for verification examples.
Event Structure
{
"id": "evt_unique_event_id",
"type": "payment.confirmed",
"created_at": "2026-07-15T17:15:00.000Z",
"livemode": true,
"data": {
"reference": "INV-2026-0847",
"amount": 25000,
"status": "CONFIRMED",
"metadata": {}
}
}
Event Types
| Event | Trigger |
|---|---|
checkout.session.completed | Checkout session completed (confirmed payment) |
payment.confirmed | Auto-confirmed, sandbox-confirmed, or manually verified |
payment.pending_review | Auto-confirm failed — requires admin review |
payment.failed | Payment failed or sandbox failed outcome |
payment.refunded | Full refund completed (exactly once per completed refund) |
Use GET /v1/events to inspect immutable events and delivery attempts. Dashboard merchants can also use Developers → Events.