Overview
Outpost Tax of Record provides a REST API that automates tax registration, calculation, and filings while you maintain your own checkout and payment service providers (Stripe, Adyen, etc.). The integration is server-to-server — your frontend never calls Outpost directly.
Build with AI
Copy the full integration brief and paste it into your AI coding assistant to scaffold the integration automatically.
One-click brief for your AI agent.
Integration Flow
- Frontend initiates checkout and calls your backend
- Your backend authenticates with Outpost using OAuth2 client_credentials
- Backend calculates tax via Outpost API and displays it at checkout
- After payment succeeds, backend stores the transaction with Outpost
- Outpost handles all tax filing, compliance, and liability
Security & Operational Principles
- Server-only secrets — OUTPOST_CLIENT_SECRET must never be exposed to browsers or logs
- Token management — Cache access tokens with TTL, refresh on expiry or 401 responses
- Idempotency — Prevent double confirm/cancel calls using merchantTransactionReference tracking
- Timeouts & retries — 5s timeout per request, retry transient errors (network/5xx) with bounded backoff
- Production gates — Store calls only after PSP authorization/capture succeeds
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 Tax Calculation
/api/tax/calculationsCreates a tax calculation for a given set of line items and customer information. The calculation determines applicable tax rates based on the customer's location and can be used to create a transaction after the successful payment.
Request Headers
| Parameter | Type | Required | Description |
|---|---|---|---|
| Authorization | — | Yes | Bearer token for authentication |
| Content-Type | — | Yes | application/json |
Request Body
{
"currency": "EUR",
"taxBehavior": "EXCLUSIVE",
"customer": {
"merchantCustomerReference": "CUST-12345",
"firstName": "John",
"lastName": "Doe",
"email": "john.doe@example.com",
"taxIdentifiers": [
{
"type": "EU_VAT",
"code": "NL123456789B01"
}
],
"billingAddress": {
"line1": "Keizersgracht 123",
"line2": "Apt 4B",
"city": "Amsterdam",
"postalCode": "1015 CJ",
"country": "NL"
}
},
"lineItems": [
{
"merchantLineItemReference": "ITEM-001",
"productCode": "TEST-SKU-005",
"description": "Premium Subscription - Annual",
"unitPrice": 99.99,
"quantity": 1,
"discountAmount": 10.00
}
],
"evidence": {
"billingCountry": "NL",
"ipAddress": "185.23.108.42",
"paymentMethodCountry": "NL"
}
}Request Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| currency | string | Yes | ISO 4217 currency code (e.g., EUR, USD, GBP, BRL). Brazil B2C NFS-e requires BRL. |
| taxBehavior | string enum | No | Allowed values: EXCLUSIVE, INCLUSIVE. Defaults to EXCLUSIVE. Brazil B2C NFS-e requires INCLUSIVE; see Brazil notes for country-specific amount behavior. |
| customer | object | Yes | Customer information object |
| customer.merchantCustomerReference | string | No | Your internal customer identifier |
| customer.firstName | string | Conditional | Customer's first name. Required for Brazil B2C NFS-e. |
| customer.lastName | string | Conditional | Customer's last name. Required for Brazil B2C NFS-e. |
| customer.email | string | No | Customer's email address |
| customer.taxIdentifiers | array | Conditional | Customer tax identifiers, discriminated on type. Required for Brazil NFS-e calculations. |
| customer.taxIdentifiers[].type | string enum | Conditional | Identifier type: BR_CPF, BR_CNPJ, EU_VAT, GB_VAT, or US_EIN. The jurisdiction is part of the type — there is no country field. Unknown types are rejected. For Brazil B2C NFS-e, use BR_CPF. |
| customer.taxIdentifiers[].code | string | Conditional | Identifier value. Validated per type: check digits for BR_CPF and BR_CNPJ, VIES format with a two-letter country prefix for EU_VAT. GB_VAT and US_EIN are informational and recorded as provided. |
| customer.taxIdentifiers[].buyerTaxRegime | string enum | Conditional | BR_CNPJ only. Allowed values: SIMPLES_NACIONAL, LUCRO_PRESUMIDO, LUCRO_REAL. Required for Brazil B2B NFS-e calculations. |
| customer.billingAddress | object | Yes | Customer's billing address |
| customer.billingAddress.line1 | string | Yes | Primary address line |
| customer.billingAddress.line2 | string | No | Secondary address line |
| customer.billingAddress.city | string | Yes | City name |
| customer.billingAddress.state | string | Conditional | State/province code (required for US and CA) |
| customer.billingAddress.postalCode | string | Yes | Postal/ZIP code |
| customer.billingAddress.country | string | Yes | ISO 3166-1 alpha-2 country code |
| customer.shippingAddress | object | No | Customer's shipping address. Recommended for physical goods — the destination drives US sales-tax sourcing. |
| customer.shippingAddress.line1 | string | No | Primary address line |
| customer.shippingAddress.line2 | string | No | Secondary address line |
| customer.shippingAddress.city | string | No | City name |
| customer.shippingAddress.state | string | Conditional | State/province code (required for US and CA) |
| customer.shippingAddress.postalCode | string | No | Postal/ZIP code |
| customer.shippingAddress.country | string | No | ISO 3166-1 alpha-2 country code |
| lineItems | array | Yes | Array of line items (minimum 1) |
| lineItems[].merchantLineItemReference | string | Yes | Your internal line item identifier |
| lineItems[].productCode | string | Yes | Product/service code for tax classification |
| lineItems[].description | string | Yes | Human-readable item description |
| lineItems[].unitPrice | decimal | Yes | Price per unit (must be ≥ 0) |
| lineItems[].quantity | integer | No | Quantity (default: 1, must be > 0) |
| lineItems[].discountAmount | decimal | No | Discount amount per unit. For quantity 3 and discountAmount 2.00, the total discount is 6.00; applied before tax calculation. |
| evidence | object | Conditional | Tax evidence (required for non-US/non-BR billing addresses; optional for Brazil) |
| evidence.billingCountry | string | Conditional | ISO 3166-1 alpha-2 billing country code. Required for non-US/non-BR evidence; if provided for Brazil, must be BR. |
| evidence.ipAddress | string | Conditional | Customer's IP address. Required for non-US/non-BR billing addresses; optional for Brazil. |
| evidence.paymentMethodCountry | string | No | Payment method country code |
evidence is required and evidence.billingCountry must match customer.billingAddress.country. For Brazil, evidence can be omitted; if provided, evidence.billingCountry must be BR and evidence.ipAddress is optional.currency BRL, include customer first and last name, include exactly one valid BR_CPF identifier in customer.taxIdentifiers and set taxBehavior to INCLUSIVE when the submitted line amount should remain the service value and ISS tax base.Response 201 Created
{
"taxCalculationId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"createdAt": "2026-01-05T12:00:00Z",
"expiresAt": "2026-01-05T12:30:00Z",
"currency": "EUR",
"subTotalAmount": "99.99",
"discountAmount": "10.00",
"netAmount": "89.99",
"taxAmount": "18.90",
"totalAmount": "108.89",
"customer": {
"firstName": "John",
"lastName": "Doe",
"email": "john.doe@example.com",
"billingAddress": {
"line1": "Keizersgracht 123",
"line2": "Apt 4B",
"city": "Amsterdam",
"postalCode": "1015 CJ",
"country": "NL"
}
},
"lineItems": [
{
"lineItemId": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
"merchantLineItemReference": "ITEM-001",
"productCode": "TEST-SKU-005",
"description": "Premium Subscription - Annual",
"unitPrice": "99.99",
"quantity": 1,
"discountAmount": "10.00",
"itemSubTotalAmount": "99.99",
"itemNetAmount": "89.99",
"itemTotalAmount": "108.89",
"tax": {
"rate": "21.00",
"taxAmount": "18.90",
"currency": "EUR"
},
"refunded": false
}
],
"evidence": {
"billingCountry": "NL",
"ipAddress": "185.23.108.42",
"paymentMethodCountry": "NL"
},
"entity": {
"companyName": "Outpost Commerce B.V."
}
}Brazil B2C NFS-e Example
Brazil B2C NFS-e calculations require BRL, customer first and last name, and a BR_CPF tax identifier. In inclusive mode, the submitted line amount remains the service value and ISS tax base; tax is not backed out of the line amount. The response keeps unit price, subtotal, net amount, and total amount at the submitted amount and reports ISS separately.
{
"currency": "BRL",
"taxBehavior": "INCLUSIVE",
"customer": {
"merchantCustomerReference": "BR-CUST-001",
"firstName": "Beatriz",
"lastName": "Costa",
"email": "beatriz.costa@example.com",
"taxIdentifiers": [
{
"type": "BR_CPF",
"code": "<VALID_CUSTOMER_CPF>"
}
],
"billingAddress": {
"line1": "Rua Oscar Freire, 900",
"city": "Sao Paulo",
"state": "SP",
"postalCode": "01426-002",
"country": "BR"
}
},
"lineItems": [
{
"merchantLineItemReference": "BR-VAS-ITEM-001",
"productCode": "BR-VAS-001",
"description": "Value-added mobile service",
"unitPrice": 49.90,
"quantity": 1
}
],
"evidence": {
"billingCountry": "BR",
"ipAddress": "200.160.2.3",
"paymentMethodCountry": "BR"
}
}{
"taxCalculationId": "019ef95a-cc77-78da-9041-58908baabaf9",
"createdAt": "2026-01-05T12:00:00Z",
"expiresAt": "2026-01-05T12:30:00Z",
"currency": "BRL",
"subTotalAmount": "49.90",
"discountAmount": "0.00",
"netAmount": "49.90",
"taxAmount": "1.45",
"totalAmount": "49.90",
"customer": {
"firstName": "Beatriz",
"lastName": "Costa",
"email": "beatriz.costa@example.com",
"billingAddress": {
"line1": "Rua Oscar Freire, 900",
"city": "Sao Paulo",
"state": "SP",
"postalCode": "01426-002",
"country": "BR"
}
},
"lineItems": [
{
"lineItemId": "83e026ae-b270-4590-926a-03b286fc84d8",
"merchantLineItemReference": "BR-VAS-ITEM-001",
"productCode": "BR-VAS-001",
"description": "Value-added mobile service",
"unitPrice": "49.90",
"quantity": 1,
"discountAmount": "0.00",
"itemSubTotalAmount": "49.90",
"itemNetAmount": "49.90",
"itemTotalAmount": "49.90",
"tax": {
"rate": "2.90",
"taxAmount": "1.45",
"currency": "BRL"
}
}
],
"evidence": {
"billingCountry": "BR",
"ipAddress": "200.160.2.3",
"paymentMethodCountry": "BR"
},
"entity": {
"companyName": "Outpost Brazil Limitada"
}
}Brazil CPF Validation Error
If the tax identifier is missing, has a type other than BR_CPF, or fails CPF check-digit validation, the calculation request is rejected before tax is calculated.
{
"code": "invalid_argument",
"errors": [
{
"field": "customer.taxIdentifiers[0].code",
"message": "must be a valid CPF"
}
]
}Response Fields
| Parameter | Type | Required | Description |
|---|---|---|---|
| taxCalculationId | string | — | Unique identifier for the calculation |
| createdAt | string | — | ISO 8601 timestamp when calculation was created |
| expiresAt | string | — | ISO 8601 timestamp when calculation expires |
| currency | string | — | Currency code |
| subTotalAmount | decimal | — | Sum of line item amounts before discounts |
| discountAmount | decimal | — | Total discount amount applied |
| netAmount | decimal | — | Amount after discounts, before tax |
| taxAmount | decimal | — | Total tax amount |
| totalAmount | decimal | — | Final amount including tax |
| customer | object |