Overview
Outpost Hosted Onboarding lets you onboard your existing merchants to Outpost services with minimal merchant friction. You retain full control over the merchant relationship, entry point, and checkout experience. Outpost provides the onboarding infrastructure, review workflow, and operations underneath.
Integration Flow
- You initiate - Call the Outpost API with the merchant's company data to create an onboarding application
- Merchant completes - Redirect the merchant to the Outpost-hosted onboarding UI where they fill in product-specific details and submit
- Outpost reviews - Outpost reviews the application asynchronously and approves or rejects it. You poll the application status
Listing Your Merchants
Merchants you onboard here are also readable through the partner merchant endpoints: GET /partner/api/merchants lists every merchant linked to your partner account, and GET /partner/api/merchants/{merchantId} returns one. Both are documented on the API Onboarding page. Both return merchant identity and company data, with no status. For the application status by merchant, use GET /partner/api/merchants/{merchantId}/application, which reports the lowercase set (created, submitted, approved, and so on), while the endpoints on this page return the full uppercase status below.
Base URL
| Environment | Base URL |
|---|---|
| Staging | https://api.outpostnow.tech |
| Production | https://api.outpostanywhere.com |
All endpoints use the /partner/api prefix.
Authentication
All Partner API requests require authentication using a JWT Bearer token provided in the Authorization header.
Request Header
Authorization: Bearer <your_access_api_token>
Key Points
- Create and manage your API credentials (client ID and client secret) in the Partner Portal
- You can have up to 2 active API credentials at a time, supporting safe key rotation
- Include the JWT Bearer token in the Authorization header of every request
- Keep your API credentials secure - do not expose them in client-side code or public repositories
Example Request
# Include your JWT Bearer token in every request
curl -X GET "https://api.outpostanywhere.com/partner/api/onboarding/applications/{applicationId}" \
-H "Authorization: Bearer $API_TOKEN"Initiate Onboarding
/partner/api/onboarding/initiateCreates a new onboarding application for a merchant, or resumes an existing one. This endpoint is idempotent on merchantReference alone: calling it again with the same merchantReference returns the existing application. productCode is not part of the key, so a second call with a different product returns the existing application with its original product. Use a different merchantReference if you need a separate application.
Request Headers
| Parameter | Type | Required | Description |
|---|---|---|---|
| Authorization | - | Yes | Bearer token for authentication |
| Content-Type | - | Yes | application/json |
Request Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| merchantReference | string | Yes | Your unique identifier for this merchant |
| productCode | string | Yes | Product to onboard for (e.g., tor for Tax of Record) |
| mode | string | No | Onboarding mode. Use HOSTED |
| returnUrl | string | No | URL to redirect the merchant after completing hosted onboarding |
| company | object | Yes | Merchant company information |
| company.legalName | string | Yes | Legal entity name |
| company.tradingName | string | No | Trading or brand name |
| company.website | string | Yes | Company website URL |
| company.taxId | string | Yes | Tax identification number (e.g., VAT number) |
| company.registrationNumber | string | Yes | Company registration number |
| company.jurisdiction | string | Yes | Country of incorporation (ISO 3166-1 alpha-2) |
| company.gmvTier | string | No | Gross Merchandise Volume tier |
| company.registeredAddress | object | Yes | Registered business address |
| company.registeredAddress.line1 | string | Yes | Primary address line |
| company.registeredAddress.line2 | string | No | Secondary address line |
| company.registeredAddress.city | string | Yes | City |
| company.registeredAddress.state | string | No | State or province |
| company.registeredAddress.postalCode | string | Yes | Postal or ZIP code |
| company.registeredAddress.country | string | Yes | ISO 3166-1 alpha-2 country code |
| complianceSummary | object | No | Pre-existing compliance data from your KYC process |
| merchantContext | map | No | Arbitrary key-value metadata about the merchant |
Response 200 OK
{
"applicationId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"merchantId": "9b1f4c8e-6d2a-4f7b-8e3c-1a5d0f2b7c94",
"status": "DRAFT",
"mode": "HOSTED",
"isExistingApplication": false,
"onboardingUrl": "https://onboarding.outpostanywhere.com/your-company/onboarding?app=f47ac10b-...&token=..."
}Response Fields
| Parameter | Type | Required | Description |
|---|---|---|---|
| applicationId | string | - | Unique identifier for the onboarding application (UUID) |
| merchantId | string | - | Unique identifier for the merchant Outpost created (UUID). Store it for the merchant-scoped endpoints, such as the tax APIs |
| status | string | - | Current application status |
| mode | string | - | Onboarding mode (HOSTED) |
| isExistingApplication | boolean | - | true if an existing application was returned |
| onboardingUrl | string | - | Hosted onboarding URL to redirect the merchant to |
Error Responses
| HTTP | Code | Description |
|---|---|---|
| 400 | invalid_argument | Missing or invalid required company fields |
Code Example
curl -X POST "https://api.outpostanywhere.com/partner/api/onboarding/initiate" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"merchantReference": "mrc_123456",
"productCode": "tor",
"mode": "HOSTED",
"returnUrl": "https://your-app.example.com/onboarding/complete",
"company": {
"legalName": "Acme Digital Ltd",
"tradingName": "Acme",
"website": "https://acme.example",
"taxId": "GB123456789",
"registrationNumber": "12345678",
"jurisdiction": "GB",
"registeredAddress": {
"line1": "1 Example Street",
"city": "London",
"postalCode": "EC1A 1AA",
"country": "GB"
}
}
}' | jqGet Application
/partner/api/onboarding/applications/{applicationId}Returns the current state of an onboarding application. This is the call you poll for the status.
Response Fields
| Parameter | Type | Required | Description |
|---|---|---|---|
| applicationId | string | - | Unique identifier (UUID) |
| merchantId | string | - | The Outpost merchant this application belongs to (UUID). Store it for the merchant-scoped endpoints, such as the tax APIs |
| status | string | - | Current status (DRAFT, IN_REVIEW, CHANGES_REQUESTED, APPROVED, SIGNED_BY_MERCHANT, SIGNED_BY_OUTPOST, REJECTED) |
| mode | string | - | Onboarding mode (HOSTED) |
| partnerId | string | - | Your partner identifier |
| partnerMerchantReference | string | - | The merchantReference you provided at initiation, echoed back. This is your own reference. The Outpost merchant UUID is merchantId above |
| productCode | string | - | The productCode you sent at initiation (e.g., tor), echoed back |
| company | object | - | Merchant company information provided at initiation |
| complianceSummary | object | - | The compliance summary you sent at initiation. Absent if you sent none |
| merchantContext | map | - | The metadata you sent at initiation. Absent if you sent none |
| selectedRegions | string[] | - | Regions selected by the merchant |
| businessDescription | string | - | Merchant's business description |
| storeUrls | string[] | - | Merchant's store or checkout URLs |
| legalRepresentative | PartnerContact | - | The legal representative on the application, with fullName, email and role. Absent until you or the merchant sets one |
| contacts | PartnerContacts | - | financeContact and technicalContact, each with fullName, email and role. Absent until at least one is set |
| requiredFields | string[] | - | Fixed list of the fields the flow requires, including legalRepresentative.fullName and legalRepresentative.email. The same list on every application whatever the product, so a mor application lists the legalRepresentative entries too even though only tor requires them. It never shrinks as data is filled in, and it does not tell you what still blocks submission |
| audit | object | - | Timestamps for application lifecycle events |
| audit.createdAt | string | - | ISO 8601 timestamp when the application was created |
| audit.updatedAt | string | - | ISO 8601 timestamp of the last update |
| audit.submittedAt | string | - | Timestamp when merchant submitted. Absent until the merchant submits |
| audit.reviewedAt | string | - | Timestamp when Outpost reviewed. Absent until Outpost reviews |
| audit.reviewMessage | string | - | Reviewer message (e.g., rejection reason, requested changes). Absent until a reviewer writes one |
Response 200 OK
{
"applicationId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"merchantId": "9b1f4c8e-6d2a-4f7b-8e3c-1a5d0f2b7c94",
"status": "DRAFT",
"mode": "HOSTED",
"partnerId": "partner_example",
"partnerMerchantReference": "mrc_123456",
"productCode": "tor",
"company": {
"legalName": "Acme Digital Ltd",
"jurisdiction": "GB",
"...": "..."
},
"complianceSummary": {
"verificationStatus": "VERIFIED",
"riskLevel": "LOW",
"countryOfIncorporation": "GB",
"hasOpenComplianceFlags": false
},
"merchantContext": {
"pspAccountStatus": "ACTIVE"
},
"selectedRegions": [
"GB",
"DE"
],
"businessDescription": "Subscription software for finance teams",
"storeUrls": [
"https://acme.example/checkout"
],
"legalRepresentative": {
"fullName": "Jane Doe",
"email": "jane.doe@acme.example",
"role": "Managing Director"
},
"contacts": {
"financeContact": {
"fullName": "Frank Smith",
"email": "finance@acme.example",
"role": "CFO"
},
"technicalContact": {
"fullName": "Tara Jones",
"email": "tech@acme.example",
"role": "CTO"
}
},
"requiredFields": [
"company.legalName",
"company.website",
"company.taxId",
"company.registrationNumber",
"company.jurisdiction",
"company.registeredAddress",
"company.registeredAddress.line1",
"company.registeredAddress.city",
"company.registeredAddress.postalCode",
"company.registeredAddress.country",
"legalRepresentative.fullName",
"legalRepresentative.email"
],
"audit": {
"createdAt": "2026-03-17T10:00:00Z",
"updatedAt": "2026-03-17T11:00:00Z"
}
}Fields with no value are left out of the response instead of being sent as null. In the example above submittedAt, reviewedAt and reviewMessage are missing because the merchant has not submitted yet. A client that tests value === null will see undefined. Test for a missing key instead.
Error Responses
| HTTP | Code | Description |
|---|---|---|
| 400 | invalid_argument | The applicationId in the path is not a valid UUID |
| 404 | not_found | Application not found or does not belong to your account |
Code Example
curl "https://api.outpostanywhere.com/partner/api/onboarding/applications/{applicationId}" \
-H "Authorization: Bearer $API_TOKEN" | jqUpdate Application
/partner/api/onboarding/applications/{applicationId}Updates merchant-editable fields on a draft application. Use this to pre-fill data before the merchant visits the hosted onboarding UI, or to update data after Outpost has requested changes. Only allowed when status is DRAFT or CHANGES_REQUESTED.
Request Body (all fields optional)
| Parameter | Type | Required | Description |
|---|---|---|---|
| selectedRegions | string[] | No | ISO 3166-1 alpha-2 country codes for desired regions |
| businessDescription | string | No | Description of the merchant's business |
| storeUrls | string[] | No | Merchant's store or checkout URLs |
| category | string | No | Business category |
| legalRepresentative | PartnerContact | No | The person authorised to act for the merchant, with fullName, email and role. A tor application cannot be submitted without a non-blank fullName and a valid email. Sending it replaces the whole object, so send every field you want to keep. The merchant fills the same fields in the hosted UI, so what you send here is what they see |
| contacts | PartnerContacts | No | financeContact and technicalContact, each with fullName, email and role. Both optional. Sending contacts replaces the pair, so send both together; leaving contacts out keeps them as they are |
Returns the full application state (same structure as Get Application).
Error Responses
| HTTP | Code | Description |
|---|---|---|
| 400 | invalid_argument | The applicationId in the path is not a valid UUID, or a contact email is not a valid address. The body names the field, for example legalRepresentative.email. Nothing in the request is stored |
| 404 | not_found | Application not found or does not belong to your account |
| 409 | invalid_status | Application is not in DRAFT or CHANGES_REQUESTED status |
Code Example
curl -X PATCH "https://api.outpostanywhere.com/partner/api/onboarding/applications/{applicationId}" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"selectedRegions": ["GB", "DE", "FR"],
"businessDescription": "SaaS platform for finance teams",
"storeUrls": ["https://acme.example/checkout"],
"legalRepresentative": {
"fullName": "Jane Doe",
"email": "jane.doe@acme.example",
"role": "Managing Director"
},
"contacts": {
"financeContact": {
"fullName": "Frank Smith",
"email": "finance@acme.example",
"role": "CFO"
},
"technicalContact": {
"fullName": "Tara Jones",
"email": "tech@acme.example",
"role": "CTO"
}
}
}' | jqRegenerate Link
/partner/api/onboarding/applications/{applicationId}/regenerate-linkGenerates a new hosted onboarding URL for an application. Use this when the previous hosted token has expired (tokens are valid for 30 minutes) or when you need to re-send the onboarding link to the merchant.
Response 200 OK
{
"onboardingUrl": "https://onboarding.outpostanywhere.com/your-company/onboarding?app=f47ac10b-...&token=..."
}Response Fields
| Parameter | Type | Required | Description |
|---|---|---|---|
| onboardingUrl | string | - | New hosted onboarding URL with a fresh token |
Error Responses
| HTTP | Code | Description |
|---|---|---|
| 400 | invalid_argument | The applicationId in the path is not a valid UUID |
| 404 | not_found | Application not found or does not belong to your account |
| 409 | terminal_status | Application is APPROVED, SIGNED_BY_OUTPOST or REJECTED, so no new link can be issued |
Code Example
curl -X POST "https://api.outpostanywhere.com/partner/api/onboarding/applications/{applicationId}/regenerate-link" \
-H "Authorization: Bearer $API_TOKEN" | jqApplication Status Lifecycle
Onboarding applications progress through these statuses. Regenerate Link is blocked once the application reaches APPROVED, SIGNED_BY_OUTPOST or REJECTED. APPROVED is not the end of the line: Outpost can still move an approved application back to CHANGES_REQUESTED.
Poll for status changes: there are no webhooks
Outpost does not call you back when a status changes. Poll GET /partner/api/onboarding/applications/{applicationId} on a slow schedule and stop once the status is APPROVED, SIGNED_BY_OUTPOST or REJECTED. An ops manager reviews each application, so the outcome takes as long as it takes.
Status Transitions
DRAFT
│ merchant submits
▼
IN_REVIEW ──[Outpost rejects]──> REJECTED
│ Outpost approves
▼
APPROVED ──[merchant signs]──> SIGNED_BY_MERCHANT
│ Outpost counter-signs
▼
SIGNED_BY_OUTPOST
Outpost can send an application back for changes from
IN_REVIEW, APPROVED or SIGNED_BY_MERCHANT:
CHANGES_REQUESTED ──[merchant resubmits]────> IN_REVIEW
│
└──[Outpost decides]──> APPROVED or REJECTEDStatuses
| Status | Description |
|---|---|
| DRAFT | Application created but not yet submitted by the merchant |
| IN_REVIEW | Merchant has submitted, Outpost is reviewing |
| CHANGES_REQUESTED | Outpost requested changes, and the merchant can update and resubmit |
| APPROVED | Application approved by Outpost. Outpost can still move it back to CHANGES_REQUESTED |
| SIGNED_BY_MERCHANT | The merchant has signed the Outpost agreement; Outpost has not counter-signed yet |
| SIGNED_BY_OUTPOST | Outpost has counter-signed the agreement; the merchant is onboarded |
| REJECTED | Application rejected by Outpost |
Who Triggers Each Transition
| Transition | Triggered By |
|---|---|
| DRAFT → IN_REVIEW | Merchant, via the hosted onboarding UI |
| IN_REVIEW → APPROVED | Outpost operations team |
| IN_REVIEW → REJECTED | Outpost operations team |
| IN_REVIEW → CHANGES_REQUESTED | Outpost operations team |
| CHANGES_REQUESTED → IN_REVIEW | Merchant, via the hosted onboarding UI |
| CHANGES_REQUESTED → APPROVED | Outpost operations team |
| CHANGES_REQUESTED → REJECTED | Outpost operations team |
| APPROVED → CHANGES_REQUESTED | Outpost operations team |
| APPROVED → SIGNED_BY_MERCHANT | Merchant, by signing the Outpost agreement. Signing happens in the Outpost merchant dashboard after approval, not in the hosted onboarding form. |
| SIGNED_BY_MERCHANT → SIGNED_BY_OUTPOST | Outpost operations team |
| SIGNED_BY_MERCHANT → CHANGES_REQUESTED | Outpost operations team |
What You Should Do at Each Status
| Status | Your Action |
|---|---|
| DRAFT | Wait for the merchant to visit the hosted URL and submit. Optionally pre-fill data via PATCH. |
| IN_REVIEW | No action needed. Wait for Outpost review. |
| CHANGES_REQUESTED | Notify the merchant. Check audit.reviewMessage for details. Regenerate the link if expired. |
| APPROVED | Merchant is approved. Proceed with activation in your system. Keep polling: Outpost can still request changes. |
| SIGNED_BY_MERCHANT | No action needed. Wait for Outpost to counter-sign the agreement. |
| SIGNED_BY_OUTPOST | The agreement is signed by both sides. The merchant is onboarded. |
| REJECTED | Notify the merchant. Check audit.reviewMessage for the reason. |
Data Models
Reference for the data structures used in onboarding API requests and responses.
OnboardingCompany
| Parameter | Type | Required | Description |
|---|---|---|---|
| legalName | string | Yes | Legal entity name |
| tradingName | string | No | Trading or brand name |
| website | string | Yes | Company website URL |
| taxId | string | Yes | Tax identification number |
| registrationNumber | string | Yes | Company registration number |
| jurisdiction | string | Yes | Country of incorporation (ISO 3166-1 alpha-2) |
| gmvTier | string | No | Gross Merchandise Volume tier |
| registeredAddress | OnboardingAddress | Yes | Registered business address |
OnboardingAddress
| Parameter | Type | Required | Description |
|---|---|---|---|
| line1 | string | Yes | Primary address line |
| line2 | string | No | Secondary address line |
| city | string | Yes | City |
| state | string | No | State or province |
| postalCode | string | Yes | Postal or ZIP code |
| country | string | Yes | ISO 3166-1 alpha-2 country code |
PartnerContact
Used for the legal representative and for both entries under contacts.
| Parameter | Type | Required | Description |
|---|---|---|---|
| fullName | string | No | The person’s full name |
| string | No | Email address. Rejected with 400 invalid_argument if it is not a valid address | |
| role | string | No | Job title, for example Managing Director. Free text |
PartnerContacts
| Parameter | Type | Required | Description |
|---|---|---|---|
| financeContact | PartnerContact | No | Who to contact about tax filings and invoices |
| technicalContact | PartnerContact | No | Who to contact about the integration |
PartnerComplianceSummary
| Parameter | Type | Required | Description |
|---|---|---|---|
| verificationStatus | string | No | Your verification status for this merchant |
| riskLevel | string | No | Your risk assessment level |
| countryOfIncorporation | string | No | Country of incorporation from your records |
| hasOpenComplianceFlags | boolean | No | Whether there are open compliance flags |
OnboardingGmvTier
| Value | Description |
|---|---|
| UP_TO_100K | Up to $100K annual GMV |
| FROM_100K_TO_250K | $100K to $250K |
| FROM_250K_TO_500K | $250K to $500K |
| FROM_500K_TO_1M | $500K to $1M |
| FROM_1M_TO_10M | $1M to $10M |
| FROM_10M_TO_50M | $10M to $50M |
| FROM_50M_TO_100M | $50M to $100M |
| FROM_100M_TO_250M | $100M to $250M |
| FROM_250M_TO_500M | $250M to $500M |
| FROM_500M_TO_1B | $500M to $1B |
PartnerAuditResponse
| Parameter | Type | Required | Description |
|---|---|---|---|
| createdAt | string | - | ISO 8601 timestamp when the application was created |
| updatedAt | string | - | ISO 8601 timestamp of the last update |
| submittedAt | string | - | Timestamp when the merchant submitted. Absent until the merchant submits |
| reviewedAt | string | - | Timestamp when Outpost reviewed. Absent until Outpost reviews |
| reviewMessage | string | - | Reviewer's message (e.g., rejection reason, requested changes). Absent until a reviewer writes one |
Error Handling
All error responses follow a consistent structure with a category code and field-specific error details.
Error Response Format
{
"code": "invalid_argument",
"errors": [
{
"field": "company.legalName",
"message": "required"
}
]
}Error Codes
| Code | Description |
|---|---|
| invalid_argument | One or more request fields are invalid or missing |
| not_found | The requested application was not found or does not belong to your account |
| invalid_status | The application is not in a status that allows the requested operation |
| terminal_status | The application is APPROVED, SIGNED_BY_OUTPOST or REJECTED, so Regenerate Link cannot issue a new link |
Common Error Scenarios
| Scenario | Status | Code | Resolution |
|---|---|---|---|
| Missing required company fields at initiation | 400 | invalid_argument | Include all required fields in the company object |
| Application not found | 404 | not_found | Verify the applicationId and ensure it belongs to your account |
| Updating an application in review | 409 | invalid_status | You can only update applications in DRAFT or CHANGES_REQUESTED status |
| Path applicationId is not a valid UUID | 400 | invalid_argument | Send the applicationId exactly as returned by Initiate Onboarding |
| Regenerating a link for a signed-off application | 409 | terminal_status | No new link can be issued once the application is APPROVED, SIGNED_BY_OUTPOST or REJECTED. Create a new application if needed |
Required Fields for Submission
Before the merchant can submit the application in the hosted UI, these fields must be filled (by the merchant or pre-filled by you via PATCH). The check runs only for the tor product. The field names below are the keys the error reports, which are not always the same as the request field names.
| Reported field | Description |
|---|---|
| products | No product is selected on the application. This is reported on its own, in place of the fields below |
| selectedRegions | At least one region must be selected |
| businessDescription | Business description must be provided |
| checkoutUrl | At least one store or checkout URL must be provided. Set it with the storeUrls field on Update Application |
| legalRepresentative | A legal representative must have a non-blank fullName and a valid email. The merchant enters this in the hosted UI, and you can pre-fill it on Update Application |
None of the four endpoints on this page returns this error. Submission happens in the hosted UI, which returns incomplete_application to the merchant. The API-mode onboarding endpoints return it too.