Overview
The Outpost Merchant of Record API lets you issue proforma invoices for B2B customers paying by bank transfer, receive a webhook the moment those transfers settle, and retrieve tax invoices for payments and refunds processed through Outpost. Proforma invoices are generated on demand and returned with a link to the PDF; tax invoices are generated automatically and fetched by Outpost ID or PSP reference.
Key Concepts
| Concept | Description |
|---|---|
| proforma invoice | A non-fiscal request for payment issued before funds are received. Used at checkout for B2B customers paying by bank transfer. |
| jurisdiction | The tax jurisdiction a proforma invoice is issued under (ISO 3166-1 alpha-2). Determines numbering, format, and legal text. |
| paymentId | Outpost's internal payment identifier (UUID) |
| refundId | Outpost's internal refund identifier (UUID) |
| psp_reference | The payment or refund reference from your PSP (e.g., Adyen pspReference, Stripe charge ID) |
Environment Setup
| Variable | Value | Notes |
|---|---|---|
| API_BASE_URL | https://api.outpostanywhere.com | Base URL for all API endpoints |
| CLIENT_ID | Your client ID | Non-sensitive, can be inlined |
| OUTPOST_CLIENT_SECRET | — | Store in environment variables or secret manager |
Authentication
Outpost uses OAuth2 client_credentials flow for server-to-server authentication. Your backend requests an access token using your client ID and secret, then includes it as a Bearer token in all API calls.
Flow
- Request access token using client credentials
- Parse response and extract access_token with expires_in
- Cache token server-side with TTL (recommend expires_in - 300s buffer)
- Refresh token on expiry or 401 responses
- Include Bearer token in all subsequent API requests
Cache access_token with expires_in - 300s buffer; retry once after refresh on 401.
Token Response
{
"access_token": "<JWT>",
"expires_in": 86400,
"token_type": "Bearer"
}Authentication
# Step 1: Obtain an access token
curl -X POST \
"https://access.outpostanywhere.com/oauth2/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=$OUTPOST_CLIENT_SECRET" | jq
# Export the token (example)
# export OUTPOST_ACCESS_TOKEN="eyJhbGciOi..."
# Sample response:
# {
# "access_token": "<JWT>",
# "expires_in": 86400,
# "token_type": "Bearer"
# }Run from your server. Never expose secrets in the browser.
Create Proforma Invoice
/api/proforma-invoicesGenerates a proforma invoice for a B2B customer who is paying by bank transfer. A proforma invoice is a non-fiscal request for payment: it states what is owed, to whom, and how to pay, before the money has been received. Outpost renders the PDF, stores it, and returns a link to it directly in the response so you can surface it on your checkout.
How it works
- Your checkout collects the B2B customer details, line items, and jurisdiction.
- Your backend calls POST /api/proforma-invoices with that payload.
- Outpost generates the PDF, stores it, and returns invoice.url in the response.
- You display or email the proforma invoice to the customer with bank-transfer instructions.
- When the transfer settles, Outpost sends a proforma_invoice.settled webhook so you can fulfill the order.
Request Headers
| Parameter | Type | Required | Description |
|---|---|---|---|
| Authorization | — | Yes | Bearer token for authentication |
| Content-Type | — | Yes | application/json |
Request Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| jurisdiction | string | Yes | Tax jurisdiction the invoice is issued under (ISO 3166-1 alpha-2). Determines numbering, format, and legal text. |
| total | object | Yes | Invoice total |
| total.amount | string | Yes | Total amount as a decimal string (e.g., "1111.00") |
| total.currency | string | Yes | ISO 4217 currency code (e.g., GBP, EUR, USD) |
| recipient | object | Yes | The B2B customer being invoiced |
| recipient.legalName | string | Yes | Registered legal name of the customer |
| recipient.address | object | Yes | Customer billing address |
| recipient.address.line1 | string | Yes | Primary address line |
| recipient.address.line2 | string | No | Secondary address line |
| recipient.address.locality | string | Yes | City or locality |
| recipient.address.region | string | No | State, province, or region |
| recipient.address.postalCode | string | Yes | Postal or ZIP code |
| recipient.address.country | string | Yes | ISO 3166-1 alpha-2 country code |
| recipient.taxIdentifiers | array | Yes | Customer tax identifiers (e.g., CNPJ, VAT) |
| recipient.taxIdentifiers[].type | string | Yes | Identifier type (e.g., VAT, EIN) |
| recipient.taxIdentifiers[].code | string | Yes | The identifier value |
| recipient.taxIdentifiers[].country | string | Yes | Issuing country (ISO 3166-1 alpha-2) |
| recipient.contact | object | Yes | Customer contact details |
| recipient.contact.email | string | Yes | Email address for invoice delivery |
| provider | object | Yes | The entity supplying the goods or services |
| provider.providerName | string | Yes | Legal name of the supplying entity |
| provider.providerCountry | string | Yes | Provider country (ISO 3166-1 alpha-2) |
| lineItems | array | Yes | One or more invoice line items |
| lineItems[].description | string | Yes | Description of the item |
| lineItems[].quantity | string | Yes | Quantity as a decimal string |
| lineItems[].unitOfMeasure | string | No | Unit of measure (e.g., hour, license) |
| lineItems[].unitPrice | object | Yes | Price per unit |
| lineItems[].unitPrice.amount | string | Yes | Unit price as a decimal string |
| lineItems[].unitPrice.currency | string | Yes | ISO 4217 currency code |
| lineItems[].classifications | array | No | Tax or product classification codes for the item |
| issueDate | string | Yes | Issue date (ISO 8601 date, YYYY-MM-DD) |
| dueDate | string | Yes | Payment due date (ISO 8601 date, YYYY-MM-DD) |
| notices | array | No | Free-text notices to print on the invoice (e.g., payment instructions) |
Response 201 Created
{
"proformaInvoiceId": "019d4d40-15cf-7764-ad6e-b2ec47736bb9",
"status": "ISSUED",
"invoice": {
"fileName": "proforma-invoice-2026-000123.pdf",
"url": "https://storage.outpostanywhere.com/proforma-invoices/019d4d40-...pdf",
"expiresAt": "2026-06-01T12:15:00Z"
},
"total": { "amount": "1200.00", "currency": "GBP" },
"issueDate": "2026-06-01",
"dueDate": "2026-06-15",
"createdAt": "2026-06-01T12:00:00Z"
}Response Fields
| Parameter | Type | Required | Description |
|---|---|---|---|
| proformaInvoiceId | string (UUID) | — | Outpost identifier for the proforma invoice. Use it to reconcile settlement webhooks. |
| status | string | — | Lifecycle status. ISSUED on creation; transitions to SETTLED once the bank transfer clears. |
| invoice | object | — | Generated invoice file information |
| invoice.fileName | string | — | Name of the generated proforma invoice PDF |
| invoice.url | string | — | Pre-signed link to the proforma invoice PDF. Show it on your checkout or email it to the customer. |
| invoice.expiresAt | string | — | ISO 8601 timestamp when the download link expires (15 minutes). Re-fetch the invoice to mint a fresh link. |
| total | object | — | Echoed invoice total |
| issueDate | string | — | Echoed issue date |
| dueDate | string | — | Echoed due date |
| createdAt | string | — | ISO 8601 creation timestamp |
Error Responses
| HTTP | Code | Description |
|---|---|---|
| 400 | invalid_argument | Missing or invalid required field (e.g., jurisdiction, recipient, or lineItems) |
| 401 | — | Invalid or missing Authorization token |
| 422 | unsupported_jurisdiction | Proforma invoices are not yet available for the requested jurisdiction |
Code Example
# Create a proforma invoice for a B2B customer paying by bank transfer
curl -X POST \
"https://api.outpostanywhere.com/api/proforma-invoices" \
-H "Authorization: Bearer $OUTPOST_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jurisdiction": "GB",
"total": { "amount": "1200.00", "currency": "GBP" },
"recipient": {
"legalName": "Acme Corp",
"address": {
"line1": "123 Example Street",
"line2": "",
"locality": "London",
"region": "",
"postalCode": "EC1A 1BB",
"country": "GB"
},
"taxIdentifiers": [
{ "type": "VAT", "code": "GB123456789", "country": "GB" }
],
"contact": { "email": "billing@acme.example" }
},
"provider": { "providerName": "Example Ltd", "providerCountry": "GB" },
"lineItems": [
{
"description": "Annual subscription",
"quantity": "1",
"unitOfMeasure": "",
"unitPrice": { "amount": "1200.00", "currency": "GBP" },
"classifications": []
}
],
"issueDate": "2026-06-01",
"dueDate": "2026-06-15",
"notices": []
}' | jq
# Response: 201 Created — body contains invoice.url, the link to the PDFGet Payment Invoice by ID
/api/payments/{paymentId}/invoiceRetrieves the B2C tax invoice for a payment using the Outpost payment ID.
Request Headers
| Parameter | Type | Required | Description |
|---|---|---|---|
| Authorization | — | Yes | Bearer token for authentication |
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| paymentId | string (UUID) | Yes | Outpost payment identifier |
Response 200 OK
{
"invoice": {
"fileName": "invoice-7110000000023059375.pdf",
"url": "https://storage.outpostanywhere.com/invoices/...",
"expiresAt": "2026-01-05T12:15:00Z"
}
}Response Fields
| Parameter | Type | Required | Description |
|---|---|---|---|
| invoice | object | — | Invoice file information |
| invoice.fileName | string | — | Name of the invoice PDF file |
| invoice.url | string | — | Pre-signed URL to download the invoice |
| invoice.expiresAt | string | — | ISO 8601 timestamp when the download URL expires (15 minutes) |
Response 202 Accepted — Invoice is still being generated. Retry after a short delay.
Error Responses
| HTTP | Code | Description |
|---|---|---|
| 404 | — | Payment/refund not found or does not belong to your merchant account |
| 401 | — | Invalid or missing Authorization token |
Code Example
# Get payment invoice by Outpost payment ID
curl -X GET \
"https://api.outpostanywhere.com/api/payments/${PAYMENT_ID}/invoice" \
-H "Authorization: Bearer $OUTPOST_ACCESS_TOKEN" | jq
# Response: 200 OK with invoice download URL
# Response: 202 Accepted if invoice is still being generatedGet Payment Invoice by PSP Reference
/api/payments/invoice?psp_reference={psp_reference}Retrieves the B2C tax invoice for a payment using the PSP reference (e.g., Adyen pspReference or Stripe charge ID).
Request Headers
| Parameter | Type | Required | Description |
|---|---|---|---|
| Authorization | — | Yes | Bearer token for authentication |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| psp_reference | string | Yes | Payment reference from your PSP |
Response 200 OK
{
"invoice": {
"fileName": "invoice-7110000000023059375.pdf",
"url": "https://storage.outpostanywhere.com/invoices/...",
"expiresAt": "2026-01-05T12:15:00Z"
}
}Response 202 Accepted — Invoice is still being generated. Retry after a short delay.
Error Responses
| HTTP | Code | Description |
|---|---|---|
| 404 | — | Payment/refund not found or does not belong to your merchant account |
| 401 | — | Invalid or missing Authorization token |
Code Example
# Get payment invoice by PSP reference
curl -X GET \
"https://api.outpostanywhere.com/api/payments/invoice?psp_reference=7110000000023059375" \
-H "Authorization: Bearer $OUTPOST_ACCESS_TOKEN" | jqGet Refund Invoice by ID
/api/refunds/{refundId}/invoiceRetrieves the B2C tax invoice for a refund using the Outpost refund ID.
Request Headers
| Parameter | Type | Required | Description |
|---|---|---|---|
| Authorization | — | Yes | Bearer token for authentication |
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| refundId | string (UUID) | Yes | Outpost refund identifier |
Response 200 OK
{
"invoice": {
"fileName": "invoice-7110000000023059375.pdf",
"url": "https://storage.outpostanywhere.com/invoices/...",
"expiresAt": "2026-01-05T12:15:00Z"
}
}Response 202 Accepted — Invoice is still being generated. Retry after a short delay.
Error Responses
| HTTP | Code | Description |
|---|---|---|
| 404 | — | Payment/refund not found or does not belong to your merchant account |
| 401 | — | Invalid or missing Authorization token |
Code Example
# Get refund invoice by Outpost refund ID
curl -X GET \
"https://api.outpostanywhere.com/api/refunds/${REFUND_ID}/invoice" \
-H "Authorization: Bearer $OUTPOST_ACCESS_TOKEN" | jqGet Refund Invoice by PSP Reference
/api/refunds/invoice?psp_reference={psp_reference}Retrieves the B2C tax invoice for a refund using the PSP reference.
Request Headers
| Parameter | Type | Required | Description |
|---|---|---|---|
| Authorization | — | Yes | Bearer token for authentication |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| psp_reference | string | Yes | Refund reference from your PSP |
Response 200 OK
{
"invoice": {
"fileName": "invoice-7110000000023059375.pdf",
"url": "https://storage.outpostanywhere.com/invoices/...",
"expiresAt": "2026-01-05T12:15:00Z"
}
}Response 202 Accepted — Invoice is still being generated. Retry after a short delay.
Error Responses
| HTTP | Code | Description |
|---|---|---|
| 404 | — | Payment/refund not found or does not belong to your merchant account |
| 401 | — | Invalid or missing Authorization token |
Code Example
# Get refund invoice by PSP reference
curl -X GET \
"https://api.outpostanywhere.com/api/refunds/invoice?psp_reference=7110000000023060251" \
-H "Authorization: Bearer $OUTPOST_ACCESS_TOKEN" | jqWebhooks
Bank transfers settle asynchronously — sometimes minutes, sometimes days after you issue a proforma invoice. Rather than polling, register a webhook endpoint and Outpost will notify your backend the moment the money settles, so you can release the order or activate the subscription.
Register Webhook
/api/webhooksRegisters an endpoint to receive event notifications. The response includes a secret that is shown only once — store it to verify incoming signatures.
Request Headers
| Parameter | Type | Required | Description |
|---|---|---|---|
| Authorization | — | Yes | Bearer token for authentication |
| Content-Type | — | Yes | application/json |
Request Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| url | string | Yes | HTTPS endpoint that will receive event POSTs. Must be publicly reachable. |
| events | string[] | Yes | Event types to subscribe to (e.g., proforma_invoice.settled) |
| description | string | No | Human-readable label for this endpoint |
Response 201 Created
{
"id": "wh_019d4d40-15cf-7764-ad6e-b2ec47736bb9",
"url": "https://your-app.example.com/webhooks/outpost",
"events": ["proforma_invoice.settled"],
"description": "Settlement notifications for B2B bank transfers",
"secret": "whsec_8f2b...d41a",
"status": "ACTIVE",
"createdAt": "2026-06-01T12:00:00Z"
}Response Fields
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | — | Webhook endpoint identifier. Use it to list or delete the endpoint. |
| url | string | — | The registered destination URL |
| events | string[] | — | Subscribed event types |
| secret | string | — | Signing secret. Returned once on creation — store it securely to verify the Outpost-Signature header. |
| status | string | — | ACTIVE or DISABLED |
| createdAt | string | — | ISO 8601 creation timestamp |
Error Responses
| HTTP | Code | Description |
|---|---|---|
| 400 | invalid_url | url is missing, not HTTPS, or not reachable |
| 400 | invalid_argument | events is empty or contains an unknown event type |
| 401 | — | Invalid or missing Authorization token |
Code Example
# Register an endpoint to receive settlement notifications
curl -X POST \
"https://api.outpostanywhere.com/api/webhooks" \
-H "Authorization: Bearer $OUTPOST_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.example.com/webhooks/outpost",
"events": ["proforma_invoice.settled"],
"description": "Settlement notifications for B2B bank transfers"
}' | jq
# Response: 201 Created — store the returned "secret" to verify signaturesList Webhooks
/api/webhooksReturns all webhook endpoints registered for your merchant account.
Response 200 OK
{
"webhooks": [
{
"id": "wh_019d4d40-15cf-7764-ad6e-b2ec47736bb9",
"url": "https://your-app.example.com/webhooks/outpost",
"events": ["proforma_invoice.settled"],
"status": "ACTIVE",
"createdAt": "2026-06-01T12:00:00Z"
}
]
}Code Example
# List all registered webhook endpoints
curl -X GET \
"https://api.outpostanywhere.com/api/webhooks" \
-H "Authorization: Bearer $OUTPOST_ACCESS_TOKEN" | jqDelete Webhook
/api/webhooks/{webhookId}Removes a webhook endpoint. It immediately stops receiving events.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| webhookId | string | Yes | Identifier returned when the webhook was registered |
Response 204 No Content — The webhook was deleted.
Code Example
# Delete a webhook endpoint so it stops receiving events
curl -X DELETE \
"https://api.outpostanywhere.com/api/webhooks/${WEBHOOK_ID}" \
-H "Authorization: Bearer $OUTPOST_ACCESS_TOKEN"
# Response: 204 No ContentEvents & Delivery
Each subscribed event is delivered as an HTTP POST to your endpoint with a JSON body.
Event Types
| Event | Description |
|---|---|
| proforma_invoice.settled | The bank transfer for a proforma invoice has been received and reconciled. Safe to fulfill the order. |
Example Payload
{
"id": "evt_019d4d52-7a11-7c0e-9a3b-1f2e3d4c5b6a",
"type": "proforma_invoice.settled",
"createdAt": "2026-06-12T09:30:00Z",
"data": {
"proformaInvoiceId": "019d4d40-15cf-7764-ad6e-b2ec47736bb9",
"status": "SETTLED",
"settledAt": "2026-06-12T09:29:41Z",
"total": { "amount": "1200.00", "currency": "GBP" },
"amountReceived": { "amount": "1200.00", "currency": "GBP" },
"recipient": { "legalName": "Acme Corp" }
}
}Payload Fields
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | — | Unique event identifier. Use it to deduplicate retried deliveries. |
| type | string | — | Event type (e.g., proforma_invoice.settled) |
| createdAt | string | — | ISO 8601 timestamp the event was generated |
| data.proformaInvoiceId | string | — | The proforma invoice this event relates to |
| data.status | string | — | New status of the proforma invoice (SETTLED) |
| data.settledAt | string | — | ISO 8601 timestamp the funds were reconciled |
| data.amountReceived | object | — | Amount actually received, which may differ from total on partial payment |
Delivery Semantics
- Respond with a 2xx status within 10 seconds to acknowledge receipt.
- Failed deliveries are retried with exponential backoff for up to 24 hours.
- Deliveries are at-least-once — deduplicate on the event id.
- Always verify the Outpost-Signature header before acting on a payload.
Verifying Signatures
Every delivery includes an Outpost-Signature header of the form t=<timestamp>,v1=<hmac>. Compute an HMAC-SHA256 of {t}.{rawBody} with your webhook secret and compare it against v1 in constant time. Reject requests whose timestamp is older than five minutes to prevent replay.
Verify Signature
# Outpost signs every webhook with HMAC-SHA256 over the raw request body.
# Verify the Outpost-Signature header before trusting the payload.
#
# Outpost-Signature: t=1717243200,v1=5257a869e7 ...
#
# Concatenate "{t}.{rawBody}", compute HMAC-SHA256 with your webhook secret,
# and compare it to v1 in constant time. See the language tabs for a full example.Use cases
End-to-end integration guides for common patterns built on top of the Merchant of Record API.