Download OpenAPI specification:Download
API for enrolling businesses that do not appear in national registries, e.g. sole traders and government departments.
To create trade accounts or orders for such businesses, a proposal must first be created and then submitted via this API for Two to enrol the business.
This approach uses Two's hosted popup to handle both returning buyers and new registrations.
Step 1: Check for existing buyer data
From your server, obtain an Autofill API delegation token (POST /autofill/v1/delegation) with read_current_buyer: true.
Pass this token to the buyer's browser and call GET /autofill/v1/buyer/current with the token in the
Two-Delegated-Authority-Token header. If the buyer has previously saved their details on this device, their
business information is returned - use it directly without further steps.
Step 2: If no autofill data, obtain delegation tokens for the popup
From your server, request delegation tokens from both APIs:
Business Registration API (POST /registry/v1/delegation):
verify_returning_buyer: true - allows email verification of returning buyerscreate_proposal: true - allows new buyer registrationAutofill API (POST /autofill/v1/delegation):
write_current_buyer: true - allows the popup to save buyer details on completionread_current_buyer: true - allows retrieval after completionStep 3: Open the signup popup
Pass both tokens to the buyer's browser and open a popup to https://checkout.two.inc/soletrader/signup with:
| Parameter | Required | Description |
|---|---|---|
authToken |
Yes | Delegation token from Business Registration API |
autofillToken |
Yes | Delegation token from Autofill API |
autofillData |
No | URL-encoded JSON of buyer details to pre-populate the form |
The popup handles email verification for returning buyers, or guides new buyers through registration.
Step 4: Retrieve buyer details
When the popup closes, call GET /autofill/v1/buyer/current with your Autofill delegation token to retrieve
the buyer's business details (company name, country code, organization number, addresses).
For full control over the user experience, integrate directly with the API endpoints.
Returning buyer flow:
Before creating a new proposal, check if the buyer already has a registered business:
POST /proposal/returning-buyer/verification with the buyer's email addressPATCH /proposal/returning-buyer/verificationNew buyer registration flow:
If no existing business is found:
POST /registry/v1/proposalDRAFTDRAFT statusProposal lifecycle:
| Status | Description |
|---|---|
DRAFT |
Initial state. Evidence can be uploaded. |
SUBMITTED |
Automated evaluation in progress. |
IN_REVIEW |
Manual review required. |
ACCEPTED |
Approved. Synthetic organization number generated. |
DECLINED |
Rejected. |
WITHDRAWN |
Cancelled via DELETE while SUBMITTED or IN_REVIEW. |
Retrieving the organization number:
Once the proposal is accepted, Two generates a synthetic organization number. There are two ways to retrieve it:
Webhook (recommended): Configure a webhook to receive enrollment.approved events. The webhook
payload includes the organization_number directly, eliminating the need to poll.
Polling: Call GET /registry/v1/proposal/<built-in function id> periodically. When status becomes ACCEPTED,
the enrolled_business field will be populated with the business details including organization_number.
Use the organization number on other Two APIs (trade accounts, orders, etc.) exactly like a registered business number.
ACCEPTED or DECLINED status.The following shell commands demonstrate the complete popup integration flow. Run each step individually (not as a script) to allow time for browser interactions.
Prerequisites: Set your API key as an environment variable:
AUTH_HEADERS="X-API-Key: <your sandbox api key>"
API_BASE="https://api.sandbox.two.inc"
CHECKOUT_BASE="https://checkout.sandbox.two.inc"
Step 1: Check for existing buyer data
# Back-end: Get a read token for the Autofill API
AUTOFILL_READ_TOKEN=$(curl -s "$API_BASE/autofill/v1/delegation" \
-H "$AUTH_HEADERS" \
-H "Content-Type: application/json" \
-d '{"read_current_buyer": true}' \
-D - -o /dev/null | grep -i "two-delegated-authority-token:" | cut -d' ' -f2 | tr -d '\r')
# Front-end: Check if buyer has saved details available
open "$API_BASE/autofill/v1/buyer/current?Two-Delegated-Authority-Token=$AUTOFILL_READ_TOKEN"
If a 404 is returned, the buyer has no saved data. Proceed to Step 2.
Step 2: Open the signup popup
# Back-end: Get tokens for registration and autofill write access
AUTH_TOKEN=$(curl -s "$API_BASE/registry/v1/delegation" \
-H "$AUTH_HEADERS" \
-H "Content-Type: application/json" \
-d '{"verify_returning_buyer": true, "create_proposal": true}' \
-D - -o /dev/null | grep -i "two-delegated-authority-token:" | cut -d' ' -f2 | tr -d '\r')
AUTOFILL_WRITE_TOKEN=$(curl -s "$API_BASE/autofill/v1/delegation" \
-H "$AUTH_HEADERS" \
-H "Content-Type: application/json" \
-d '{"write_current_buyer": true}' \
-D - -o /dev/null | grep -i "two-delegated-authority-token:" | cut -d' ' -f2 | tr -d '\r')
# Front-end: Open the signup popup (complete the flow in the browser)
open "$CHECKOUT_BASE/soletrader/signup?authToken=$AUTH_TOKEN&autofillToken=$AUTOFILL_WRITE_TOKEN"
The popup will:
Step 3: Retrieve the buyer's details
# Front-end: After the popup closes, call autofill again (reusing the existing read token)
open "$API_BASE/autofill/v1/buyer/current?Two-Delegated-Authority-Token=$AUTOFILL_READ_TOKEN"
Interpreting the result:
organization_number, country_code and company_name to place orders.Create a new proposal for a business to be enrolled with Two, or for an existing enrollment to be updated. If this operation is authorized by a delegated authority token (generated via POST .../delegation with a body of {"create_proposal": true} and passed as a request header called Two-Delegated-Authority-Token) rather than by the merchant's API key, the response will include a new delegated authority token as a response header called Two-Delegated-Authority-Token which may be used to track (GET .../proposal/<id>), withdraw (DELETE .../proposal/<id>) or submit (POST .../proposal/<id>/submission) the newly created proposal.
Idempotency-Key (string) or Idempotency-Key (null) (Idempotency-Key) Default: null |
required | any (Proposed Business) Details of the business that is being proposed for enrollment or update |
Proposal has been accepted for review.
Proposal is invalid.
{- "proposed_business": {
- "business_type": "SOLE_TRADER",
- "country_code": "AD",
- "trading_name": "string",
- "website": null,
- "email": "string",
- "phone": "string",
- "tax_number": null,
- "business_address": {
- "apartment": null,
- "building": "string",
- "street": "string",
- "postal_code": "string",
- "city": "string",
- "region": null,
- "country_code": "AD"
}, - "proprietor_details": {
- "title": null,
- "first_name": "string",
- "middle_names": null,
- "last_name": "string",
- "email": null,
- "phone": null,
- "date_of_birth": "2019-08-24",
- "address_history": [
- {
- "apartment": null,
- "building": "string",
- "street": "string",
- "postal_code": "string",
- "city": "string",
- "region": null,
- "country_code": "AD",
- "date_from": "2019-08-24",
- "date_until": null
}
], - "national_identity_numbers": null
}
}
}{- "id": null,
- "proposed_business": {
- "business_type": "SOLE_TRADER",
- "country_code": "AD",
- "trading_name": "string",
- "website": null,
- "email": "string",
- "phone": "string",
- "tax_number": null,
- "business_address": {
- "apartment": null,
- "building": "string",
- "street": "string",
- "postal_code": "string",
- "city": "string",
- "region": null,
- "country_code": "AD"
}, - "proprietor_details": null
}, - "enrolled_business": null,
- "status": "DRAFT"
}Get all matching proposals. At least one search parameter must be provided. Note that each merchant may only access proposals lodged by the same merchant. Also note that supplementary information is only included for proposals that have not yet been accepted, declined or withdrawn.
Proposals
{- "items": [
- {
- "id": null,
- "proposed_business": {
- "business_type": "SOLE_TRADER",
- "country_code": "AD",
- "trading_name": "string",
- "website": null,
- "email": "string",
- "phone": "string",
- "tax_number": null,
- "business_address": {
- "apartment": null,
- "building": "string",
- "street": "string",
- "postal_code": "string",
- "city": "string",
- "region": null,
- "country_code": "AD"
}, - "proprietor_details": null
}, - "enrolled_business": null,
- "status": "DRAFT"
}
], - "page": {
- "items": 0,
- "limit": null,
- "current": null,
- "pages": null
}
}Get an existing proposal by id. Note that each merchant may only access proposals lodged by the same merchant. Also note that supplementary information is only included for proposals that have not yet been accepted, declined or withdrawn.
Proposal
Proposal not found
{- "id": null,
- "proposed_business": {
- "business_type": "SOLE_TRADER",
- "country_code": "AD",
- "trading_name": "string",
- "website": null,
- "email": "string",
- "phone": "string",
- "tax_number": null,
- "business_address": {
- "apartment": null,
- "building": "string",
- "street": "string",
- "postal_code": "string",
- "city": "string",
- "region": null,
- "country_code": "AD"
}, - "proprietor_details": null
}, - "enrolled_business": null,
- "status": "DRAFT"
}Withdraw an existing proposal by id. Withdrawals will only be accepted for proposals with a status of IN_REVIEW. Withdrawal will not delete the proposal record but will instead update its status to WITHDRAWN after which no further updates will be possible. Withdrawal attempts will result in a 409 response if the proposal has already been approved or declined. Subsequent attempts to withdraw the same proposal will not prompt any action, and will result in a 200 status. Note that each merchant may only withdraw proposals lodged by that same merchant.
Proposal
Proposal not found
Proposal is not in a suitable state for withdrawals
{- "id": null,
- "proposed_business": {
- "business_type": "SOLE_TRADER",
- "country_code": "AD",
- "trading_name": "string",
- "website": null,
- "email": "string",
- "phone": "string",
- "tax_number": null,
- "business_address": {
- "apartment": null,
- "building": "string",
- "street": "string",
- "postal_code": "string",
- "city": "string",
- "region": null,
- "country_code": "AD"
}, - "proprietor_details": null
}, - "enrolled_business": null,
- "status": "DRAFT"
}Submit an existing proposal for review, by id. Submissions will only be accepted for proposals with a status of DRAFT. Submission attempts will result in a 200 response but take no action if the proposal has already progressed beyond DRAFT.
Proposal
Proposal not found
Proposal is not in a suitable state for submission
{- "initiation_method": "EMAIL"
}{- "initiation_method": "EMAIL",
- "proposal_id": "The id of the proposal for which verification has been initiated."
}Get all matching businesses that have previously been enrolled with Two via completed proposals. At least one search parameter must be provided. Note that each merchant may only access businesses referred to by accepted proposals lodged by the same merchant.
Matching businesses
Parameters are invalid.
{- "items": [
- {
- "business_type": "SOLE_TRADER",
- "country_code": "AD",
- "trading_name": "string",
- "website": null,
- "email": "string",
- "phone": "string",
- "tax_number": null,
- "business_address": {
- "apartment": null,
- "building": "string",
- "street": "string",
- "postal_code": "string",
- "city": "string",
- "region": null,
- "country_code": "AD"
}, - "id": "string",
- "organization_number": "string"
}
], - "page": {
- "items": 0,
- "limit": null,
- "current": null,
- "pages": null
}
}Get a business that has previously been enrolled with Two via a completed proposal. Note that each merchant may only access businesses referred to by accepted proposals lodged by the same merchant.
Business
Business not found
{- "business_type": "SOLE_TRADER",
- "country_code": "AD",
- "trading_name": "string",
- "website": null,
- "email": "string",
- "phone": "string",
- "tax_number": null,
- "business_address": {
- "apartment": null,
- "building": "string",
- "street": "string",
- "postal_code": "string",
- "city": "string",
- "region": null,
- "country_code": "AD"
}, - "id": "string",
- "organization_number": "string"
}Checks the validity of a browser delegation token, returning the token data if valid.
Delegated authority token data in the response body
Invalid request, for instance if provided Two-Delegated-Authority-Token has expired
Create a token for use within a buyer's browser. The token is returned as a response header called Two-Delegated-Authority-Token. To use the token, browser-side code should simply add the header to the relevant requests. The token will grant access for 1 hour, for the user's browser to impersonate the merchant for the operations associated with each role specified in the body of this request. No other operations will be possible.
Delegated authority token created and returned in response header named Two-Delegated-Authority-Token
Invalid request, for instance no roles specified.
{- "verify_returning_buyer": true,
- "create_proposal": true
}Check if a business is already registered with the provided email address. If found, initiate email verification by sending a verification code to that address. Use this endpoint to identify returning buyers before creating a new proposal. If a 404 is returned, no existing registration was found and you should proceed with proposal creation for a new buyer. Requires a delegation token with verify_returning_buyer permission.
Verification initiated. A verification code has been sent to the email address.
Invalid request.
No existing business found for this email. Proceed with new registration.
{- "verification_type": "EMAIL",
- "email": "user@example.com"
}Validate the verification code sent to the buyer's email. If successful and a matching business is found, returns the buyer's business details (company name, organization number, addresses, etc.). Use the returned details to identify the buyer without requiring them to re-register. Requires a delegation token with verify_returning_buyer permission.
Verification successful. Returns the buyer's business details if a match was found, or an empty object if verification succeeded but no business details are available.
Invalid verification code or request.
Too many verification attempts. Please try again later.
{- "verification_type": "EMAIL",
- "verification_id": null,
- "code": "string",
- "context": null,
- "email": null
}