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

ConceptDescription
proforma invoiceA non-fiscal request for payment issued before funds are received. Used at checkout for B2B customers paying by bank transfer.
jurisdictionThe tax jurisdiction a proforma invoice is issued under (ISO 3166-1 alpha-2). Determines numbering, format, and legal text.
paymentIdOutpost's internal payment identifier (UUID)
refundIdOutpost's internal refund identifier (UUID)
psp_referenceThe payment or refund reference from your PSP (e.g., Adyen pspReference, Stripe charge ID)

Environment Setup

VariableValueNotes
API_BASE_URLhttps://api.outpostanywhere.comBase URL for all API endpoints
CLIENT_IDYour client IDNon-sensitive, can be inlined
OUTPOST_CLIENT_SECRETStore 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

POST/api/proforma-invoices

Generates 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

ParameterTypeRequiredDescription
AuthorizationYesBearer token for authentication
Content-TypeYesapplication/json

Request Body Parameters

ParameterTypeRequiredDescription
jurisdictionstringYesTax jurisdiction the invoice is issued under (ISO 3166-1 alpha-2). Determines numbering, format, and legal text.
totalobjectYesInvoice total
total.amountstringYesTotal amount as a decimal string (e.g., "1111.00")
total.currencystringYesISO 4217 currency code (e.g., GBP, EUR, USD)
recipientobjectYesThe B2B customer being invoiced
recipient.legalNamestringYesRegistered legal name of the customer
recipient.addressobjectYesCustomer billing address
recipient.address.line1stringYesPrimary address line
recipient.address.line2stringNoSecondary address line
recipient.address.localitystringYesCity or locality
recipient.address.regionstringNoState, province, or region
recipient.address.postalCodestringYesPostal or ZIP code
recipient.address.countrystringYesISO 3166-1 alpha-2 country code
recipient.taxIdentifiersarrayYesCustomer tax identifiers (e.g., CNPJ, VAT)
recipient.taxIdentifiers[].typestringYesIdentifier type (e.g., VAT, EIN)
recipient.taxIdentifiers[].codestringYesThe identifier value
recipient.taxIdentifiers[].countrystringYesIssuing country (ISO 3166-1 alpha-2)
recipient.contactobjectYesCustomer contact details
recipient.contact.emailstringYesEmail address for invoice delivery
providerobjectYesThe entity supplying the goods or services
provider.providerNamestringYesLegal name of the supplying entity
provider.providerCountrystringYesProvider country (ISO 3166-1 alpha-2)
lineItemsarrayYesOne or more invoice line items
lineItems[].descriptionstringYesDescription of the item
lineItems[].quantitystringYesQuantity as a decimal string
lineItems[].unitOfMeasurestringNoUnit of measure (e.g., hour, license)
lineItems[].unitPriceobjectYesPrice per unit
lineItems[].unitPrice.amountstringYesUnit price as a decimal string
lineItems[].unitPrice.currencystringYesISO 4217 currency code
lineItems[].classificationsarrayNoTax or product classification codes for the item
issueDatestringYesIssue date (ISO 8601 date, YYYY-MM-DD)
dueDatestringYesPayment due date (ISO 8601 date, YYYY-MM-DD)
noticesarrayNoFree-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

ParameterTypeRequiredDescription
proformaInvoiceIdstring (UUID)Outpost identifier for the proforma invoice. Use it to reconcile settlement webhooks.
statusstringLifecycle status. ISSUED on creation; transitions to SETTLED once the bank transfer clears.
invoiceobjectGenerated invoice file information
invoice.fileNamestringName of the generated proforma invoice PDF
invoice.urlstringPre-signed link to the proforma invoice PDF. Show it on your checkout or email it to the customer.
invoice.expiresAtstringISO 8601 timestamp when the download link expires (15 minutes). Re-fetch the invoice to mint a fresh link.
totalobjectEchoed invoice total
issueDatestringEchoed issue date
dueDatestringEchoed due date
createdAtstringISO 8601 creation timestamp

Error Responses

HTTPCodeDescription
400invalid_argumentMissing or invalid required field (e.g., jurisdiction, recipient, or lineItems)
401Invalid or missing Authorization token
422unsupported_jurisdictionProforma 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 PDF

Get Payment Invoice by ID

GET/api/payments/{paymentId}/invoice

Retrieves the B2C tax invoice for a payment using the Outpost payment ID.

Request Headers

ParameterTypeRequiredDescription
AuthorizationYesBearer token for authentication

Path Parameters

ParameterTypeRequiredDescription
paymentIdstring (UUID)YesOutpost payment identifier

Response 200 OK

{
  "invoice": {
    "fileName": "invoice-7110000000023059375.pdf",
    "url": "https://storage.outpostanywhere.com/invoices/...",
    "expiresAt": "2026-01-05T12:15:00Z"
  }
}

Response Fields

ParameterTypeRequiredDescription
invoiceobjectInvoice file information
invoice.fileNamestringName of the invoice PDF file
invoice.urlstringPre-signed URL to download the invoice
invoice.expiresAtstringISO 8601 timestamp when the download URL expires (15 minutes)

Response 202 Accepted — Invoice is still being generated. Retry after a short delay.

Error Responses

HTTPCodeDescription
404Payment/refund not found or does not belong to your merchant account
401Invalid 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 generated

Get Payment Invoice by PSP Reference

GET/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

ParameterTypeRequiredDescription
AuthorizationYesBearer token for authentication

Query Parameters

ParameterTypeRequiredDescription
psp_referencestringYesPayment 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

HTTPCodeDescription
404Payment/refund not found or does not belong to your merchant account
401Invalid 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" | jq

Get Refund Invoice by ID

GET/api/refunds/{refundId}/invoice

Retrieves the B2C tax invoice for a refund using the Outpost refund ID.

Request Headers

ParameterTypeRequiredDescription
AuthorizationYesBearer token for authentication

Path Parameters

ParameterTypeRequiredDescription
refundIdstring (UUID)YesOutpost 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

HTTPCodeDescription
404Payment/refund not found or does not belong to your merchant account
401Invalid 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" | jq

Get Refund Invoice by PSP Reference

GET/api/refunds/invoice?psp_reference={psp_reference}

Retrieves the B2C tax invoice for a refund using the PSP reference.

Request Headers

ParameterTypeRequiredDescription
AuthorizationYesBearer token for authentication

Query Parameters

ParameterTypeRequiredDescription
psp_referencestringYesRefund 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

HTTPCodeDescription
404Payment/refund not found or does not belong to your merchant account
401Invalid 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" | jq

Webhooks

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

POST/api/webhooks

Registers an endpoint to receive event notifications. The response includes a secret that is shown only once — store it to verify incoming signatures.

Request Headers

ParameterTypeRequiredDescription
AuthorizationYesBearer token for authentication
Content-TypeYesapplication/json

Request Body Parameters

ParameterTypeRequiredDescription
urlstringYesHTTPS endpoint that will receive event POSTs. Must be publicly reachable.
eventsstring[]YesEvent types to subscribe to (e.g., proforma_invoice.settled)
descriptionstringNoHuman-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

ParameterTypeRequiredDescription
idstringWebhook endpoint identifier. Use it to list or delete the endpoint.
urlstringThe registered destination URL
eventsstring[]Subscribed event types
secretstringSigning secret. Returned once on creation — store it securely to verify the Outpost-Signature header.
statusstringACTIVE or DISABLED
createdAtstringISO 8601 creation timestamp

Error Responses

HTTPCodeDescription
400invalid_urlurl is missing, not HTTPS, or not reachable
400invalid_argumentevents is empty or contains an unknown event type
401Invalid 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 signatures

List Webhooks

GET/api/webhooks

Returns 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" | jq

Delete Webhook

DELETE/api/webhooks/{webhookId}

Removes a webhook endpoint. It immediately stops receiving events.

Path Parameters

ParameterTypeRequiredDescription
webhookIdstringYesIdentifier 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 Content

Events & Delivery

Each subscribed event is delivered as an HTTP POST to your endpoint with a JSON body.

Event Types

EventDescription
proforma_invoice.settledThe 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

ParameterTypeRequiredDescription
idstringUnique event identifier. Use it to deduplicate retried deliveries.
typestringEvent type (e.g., proforma_invoice.settled)
createdAtstringISO 8601 timestamp the event was generated
data.proformaInvoiceIdstringThe proforma invoice this event relates to
data.statusstringNew status of the proforma invoice (SETTLED)
data.settledAtstringISO 8601 timestamp the funds were reconciled
data.amountReceivedobjectAmount 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.