Business Registration API (1.0)

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.


Integration Options

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:

  1. Business Registration API (POST /registry/v1/delegation):

    • verify_returning_buyer: true - allows email verification of returning buyers
    • create_proposal: true - allows new buyer registration
  2. Autofill API (POST /autofill/v1/delegation):

    • write_current_buyer: true - allows the popup to save buyer details on completion
    • read_current_buyer: true - allows retrieval after completion

Step 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).


Option 2: Direct API integration

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:

  1. Call POST /proposal/returning-buyer/verification with the buyer's email address
    • 201: Verification code sent - a business exists for this email
    • 404: No existing business - proceed with new registration
  2. If 404, the buyer is not already registered. Skip to "New buyer registration flow".
  3. If 201, prompt the buyer for the verification code and call PATCH /proposal/returning-buyer/verification
  4. On success, the response contains the buyer's business details - use them directly

New buyer registration flow:

If no existing business is found:

  1. Collect buyer information and POST /registry/v1/proposal
  2. The proposal is created with status DRAFT
  3. Optionally upload supporting evidence while in DRAFT status
  4. Submit the proposal for review via the submission endpoint

Proposal 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:

  1. Webhook (recommended): Configure a webhook to receive enrollment.approved events. The webhook payload includes the organization_number directly, eliminating the need to poll.

  2. 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.


Notes

  • During review, Two may contact the business or merchant for additional information.
  • Personal information in proposals is redacted after reaching ACCEPTED or DECLINED status.
  • Currently limited to UK and US sole traders only.

Example: Testing the Interactive Popup Flow

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:

  1. Ask for the buyer's email and send a verification code
  2. If a registered business is found, save details to Autofill and close
  3. Otherwise, guide the buyer through registration, then save and close

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:

  • 200 with data: Business details retrieved successfully. Use organization_number, country_code and company_name to place orders.
  • 404: The buyer either cancelled, was rejected, or registration is still pending review.

Environments

Business Registration

Create proposal

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.

SecurityX-Api-Key
Request
header Parameters
Idempotency-Key (string) or Idempotency-Key (null) (Idempotency-Key)
Default: null
Request Body schema: application/json
required
any (Proposed Business)

Details of the business that is being proposed for enrollment or update

Responses
202

Proposal has been accepted for review.

400

Proposal is invalid.

post/registry/v1/proposal
Request samples
application/json
{
  • "proposed_business": {
    }
}
Response samples
application/json
{
  • "id": null,
  • "proposed_business": {
    },
  • "enrolled_business": null,
  • "status": "DRAFT"
}

Get proposals

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.

SecurityX-Api-Key
Request
query Parameters
BusinessTypeEnum (string) or null
Default: null
CountryCodeEnum (string) or null
Default: null
Email (string) or Email (null) (Email)
Default: null

Exact match only.

Limit (integer) or Limit (null) (Limit)
Default: null

The maximum number of items on a single page, ie the page size.

order_by
string (ProposalSearchOrderByEnum)
Default: "date_created"

How to order the search results

Enum: "status" "trading_name" "date_created" "date_updated"
order_desc
boolean (Order Desc)
Default: false

If true, reverse the results order.

Page (integer) or Page (null) (Page)
Default: null

The requested page number, 1-based.

Phone (string) or Phone (null) (Phone)
Default: null

Exact match only.

ProposalStatusEnum (string) or null
Default: null

If omitted, will return proposals that currently require action.

Trading Name (string) or Trading Name (null) (Trading Name)
Default: null

Case-insensitive partial match.

Responses
200

Proposals

get/registry/v1/proposal
Request samples
Response samples
application/json
{
  • "items": [
    ],
  • "page": {
    }
}

Get proposal

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.

Request
path Parameters
proposal_id
required
string

The id of an existing proposal.

Responses
200

Proposal

404

Proposal not found

get/registry/v1/proposal/{proposal_id}
Request samples
Response samples
application/json
{
  • "id": null,
  • "proposed_business": {
    },
  • "enrolled_business": null,
  • "status": "DRAFT"
}

Withdraw proposal

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.

Request
path Parameters
proposal_id
required
string

The id of an existing proposal.

Responses
200

Proposal

404

Proposal not found

409

Proposal is not in a suitable state for withdrawals

delete/registry/v1/proposal/{proposal_id}
Request samples
Response samples
application/json
{
  • "id": null,
  • "proposed_business": {
    },
  • "enrolled_business": null,
  • "status": "DRAFT"
}

Submit proposal

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.

Request
path Parameters
proposal_id
required
string

The id of an existing proposal.

Request Body schema: application/json
optional
initiation_method
string (Initiation Method)
Default: "REDIRECT"

How to direct the buyer to begin verification.

Enum: "EMAIL" "REDIRECT"
Responses
200

Proposal

404

Proposal not found

409

Proposal is not in a suitable state for submission

post/registry/v1/proposal/{proposal_id}/submission
Request samples
application/json
{
  • "initiation_method": "EMAIL"
}
Response samples
application/json
{
  • "initiation_method": "EMAIL",
  • "proposal_id": "The id of the proposal for which verification has been initiated."
}

Get businesses

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.

SecurityX-Api-Key
Request
query Parameters
BusinessTypeEnum (string) or null
Default: null
CountryCodeEnum (string) or null
Default: null
Email (string) or Email (null) (Email)
Default: null

Exact match only.

Limit (integer) or Limit (null) (Limit)
Default: null

The maximum number of items on a single page, ie the page size.

order_by
string (BusinessSearchOrderByEnum)
Default: "date_created"

How to order the search results

Enum: "organization_number" "trading_name" "date_created" "date_updated"
order_desc
boolean (Order Desc)
Default: false

If true, reverse the results order.

Organization Number (string) or Organization Number (null) (Organization Number)
Default: null

Exact match only.

Page (integer) or Page (null) (Page)
Default: null

The requested page number, 1-based.

Phone (string) or Phone (null) (Phone)
Default: null

Exact match only.

Trading Name (string) or Trading Name (null) (Trading Name)
Default: null

Case-insensitive partial match.

Responses
200

Matching businesses

400

Parameters are invalid.

get/registry/v1/business
Request samples
Response samples
application/json
{
  • "items": [
    ],
  • "page": {
    }
}

Get business

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.

SecurityX-Api-Key
Request
path Parameters
business_id
required
string

The id of a business that was enrolled as the result of an accepted proposal.

Responses
200

Business

404

Business not found

get/registry/v1/business/{business_id}
Request samples
Response samples
application/json
{
  • "business_type": "SOLE_TRADER",
  • "country_code": "AD",
  • "trading_name": "string",
  • "website": null,
  • "email": "string",
  • "phone": "string",
  • "tax_number": null,
  • "business_address": {
    },
  • "id": "string",
  • "organization_number": "string"
}

Check validity of a browser delegation token

Checks the validity of a browser delegation token, returning the token data if valid.

Responses
200

Delegated authority token data in the response body

401

Invalid request, for instance if provided Two-Delegated-Authority-Token has expired

get/registry/v1/delegation
Request samples

Create a browser delegation token

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.

Request
Request Body schema: application/json
create_proposal
boolean (Create Proposal)
Default: true

This role allows the token bearer to create new proposals.

verify_returning_buyer
boolean (Verify Returning Buyer)
Default: true

This role allows the token bearer to initiate email verification to identify returning buyers.

Responses
200

Delegated authority token created and returned in response header named Two-Delegated-Authority-Token

400

Invalid request, for instance no roles specified.

post/registry/v1/delegation
Request samples
application/json
{
  • "verify_returning_buyer": true,
  • "create_proposal": true
}

Start returning buyer verification

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.

Request
Request Body schema: application/json
email
required
string <email> (Email)
verification_type
string (Verification Type)
Default: "EMAIL"
Value: "EMAIL"
Responses
201

Verification initiated. A verification code has been sent to the email address.

400

Invalid request.

404

No existing business found for this email. Proceed with new registration.

post/registry/v1/proposal/returning-buyer/verification
Request samples
application/json
{
  • "verification_type": "EMAIL",
  • "email": "user@example.com"
}

Complete returning buyer verification

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.

Request
Request Body schema: application/json
code
required
string (Code)
VerificationContextEnum (string) or null
Default: null
Email (string) or Email (null) (Email)
Default: null
Verification Id (string) or Verification Id (null) (Verification Id)
Default: null
verification_type
string (Verification Type)
Default: "EMAIL"
Value: "EMAIL"
Responses
200

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.

400

Invalid verification code or request.

429

Too many verification attempts. Please try again later.

patch/registry/v1/proposal/returning-buyer/verification
Request samples
application/json
{
  • "verification_type": "EMAIL",
  • "verification_id": null,
  • "code": "string",
  • "context": null,
  • "email": null
}