Skip to main content

Endpoint

POST /v1/accounts

Required Fields

The minimum required fields to create an account are: contact (email, address, city, state, postal code); identity (first name, last name, date of birth, SSN/tax ID); investment_profile (risk tolerance, investment objective, income and net worth ranges). All other fields are optional but may be required for specific account types or trading features.

Full Request Schema

contact object

FieldTypeRequiredDescription
email_addressstringPrimary email address
phone_numberstringPhone with country code (e.g. +15125550100)
street_addressstring[]Street address lines
unitstringApartment, suite, or unit number
citystringCity
statestring2-letter state code (e.g. TX)
postal_codestringZIP code (5 or 9 digits)
countrystringISO 2-letter country code. Defaults to US

identity object

FieldTypeRequiredDescription
given_namestringLegal first name
middle_namestringMiddle name or initial
family_namestringLegal last name
date_of_birthstringDate of birth in YYYY-MM-DD format
tax_idstringSSN (US residents) or EIN. 9–11 characters, dashes optional.
tax_id_typestringDefaults to US_SSN. Other values: US_EIN
country_of_citizenshipstringISO country code. Defaults to US
country_of_birthstringISO country code of birth country
country_of_tax_residencestringISO country code for tax residence. Defaults to US
funding_sourcestring[]Sources of investment funds (e.g. ["employment_income"])

disclosures object

Regulatory disclosures required by FINRA. All fields default to false if omitted: is_control_person, control_person_company_name, is_affiliated_exchange_or_finra, is_spouse_affiliated_finra, is_politically_exposed, immediate_family_exposed.

agreements array

Each agreement must be captured with a timestamp and optionally IP address. The customer_agreement is required for all accounts; margin_agreement is required if margin_enabled = true; options_agreement is required if options_level > 0.
"agreements": [ { "agreement": "customer_agreement", "signed_at": "2025-06-01T12:00:00Z", "ip_address": "203.0.113.42" } ]

investment_profile object

Required: risk_tolerance (conservative/moderate/aggressive), investment_objective (growth/income/speculation/capital_preservation/other), annual_income_range, net_worth_range, liquid_net_worth_range. Optional: liquidity_needs, time_horizon (short/average/long), years_trading_stocks, years_trading_options, stock_experience, options_experience, income_source, funds_source, investment_plan.

trading_configurations object (optional)

options_level (0=none, 1=covered calls, 2=spreads, 3=full); margin_enabled (requires margin_agreement).

Other optional top-level fields

account_type (individual default or joint), trusted_contact, employment, mailing_address, joint_owner (required when account_type is joint), dividend_reinvestment, discretionary_account, registered_rep_code, advisor_code, corr_metadata (freeform, up to 255 chars).

Complete Example Request

{
  "account_type": "individual",
  "contact": { "email_address": "jane@example.com", "phone_number": "+15125550100", "street_address": ["123 Main Street"], "city": "Austin", "state": "TX", "postal_code": "78701", "country": "US" },
  "identity": { "given_name": "Jane", "middle_name": "M", "family_name": "Doe", "date_of_birth": "1990-04-15", "tax_id": "111-22-0001", "tax_id_type": "US_SSN", "country_of_citizenship": "US", "funding_source": ["employment_income"] },
  "disclosures": { "is_control_person": false, "is_affiliated_exchange_or_finra": false, "is_politically_exposed": false, "immediate_family_exposed": false },
  "agreements": [ { "agreement": "customer_agreement", "signed_at": "2025-06-01T12:00:00Z", "ip_address": "203.0.113.42" } ],
  "investment_profile": { "risk_tolerance": "moderate", "investment_objective": "growth", "annual_income_range": "50000_to_100000", "net_worth_range": "100000_to_250000", "liquid_net_worth_range": "50000_to_100000", "time_horizon": "long" },
  "trading_configurations": { "options_level": 0, "margin_enabled": false },
  "dividend_reinvestment": true,
  "corr_metadata": "internal-user-id-98765"
}

Response

On success, returns 201 Created with the full AccountResponse object including the generated id and initial status.
{
  "id": "b3f2c9a1-4d5e-4a2f-8b3c-1e2d3f4a5b6c",
  "account_number": "ACC123456789",
  "status": "SUBMITTED",
  "kyc_status": "SUBMITTED",
  "currency": "USD",
  "last_equity": "0.00",
  "created_at": "2025-06-01T12:00:05Z",
  "account_type": "individual",
  "enabled_assets": ["equity"]
}
Note: Newly created accounts start in SUBMITTED status. In the sandbox with SSN suffix 0001, KYC completes rapidly and transitions to ACTIVE. In production, KYC processing typically completes in seconds to minutes, but some accounts require manual review.

Handling KYC Outcomes

Use Webhooks to receive real-time notifications when an account’s status or kyc_status changes. Subscribe to the account.updated event and check the new status in the payload. Alternatively, poll GET /v1/accounts/{accountId} and check kyc_status until it reaches APPROVED or REJECTED.

Common Validation Errors

ErrorCause
MISSING_REQUIRED_FIELD: contact.email_addressEmail address omitted from contact
MISSING_REQUIRED_FIELD: identity.tax_idSSN omitted from identity
VALIDATION_ERROR: identity.date_of_birthDate not in YYYY-MM-DD format
MISSING_REQUIRED_FIELD: agreementsNo agreements array provided
VALIDATION_ERROR: agreements[0].signed_atTimestamp not in ISO 8601 format

Next Steps

  • Account Statuses — Full state machine and transitions
  • Balances & Positions — Reading portfolio data after the account is ACTIVE
  • Funding Overview — Link a bank account and make a deposit

Uploading Documents

The document upload endpoint allows you to submit compliance and identity documents directly to a Buildmarkets brokerage account. This is used during account onboarding when a user’s KYC (Know Your Customer) review requires supporting documentation, and during ongoing account maintenance for remediation requests or regulatory requirements. Uploaded documents are associated with the specified account and routed to the appropriate compliance workflow.

Endpoint

MethodPathDescription
POST/v1/accounts/{accountId}/documents/uploadUpload a compliance document to an account

Upload a document

Accepts a Base64-encoded file along with its document type classification. The platform processes the upload and associates it with the account for compliance review.

POST /v1/accounts/{accountId}/documents/upload

Path parameters
ParameterTypeRequiredDescription
accountIdstring (UUID)The Buildmarkets account to attach the document to
Request body
FieldTypeRequiredDescription
document_typestringClassification of the document. See supported types below
document_sub_typestringFree-form sub-classification (e.g., "passport", "drivers_license", "utility_bill")
contentstringConditionalBase64-encoded binary content of the document. Required for all types except w8ben. Maximum size: 10MB
content_dataobjectConditionalJSON form data. Used only for w8ben document type in place of content
mime_typestringMIME type of the uploaded file (e.g., "image/png", "image/jpeg", "application/pdf")
Supported document_type values
ValueDescription
identity_verificationGovernment-issued photo ID (passport, driver’s license, national ID)
address_verificationProof of address (utility bill, bank statement, lease)
date_of_birth_verificationDocument verifying date of birth
tax_id_verificationDocument verifying Social Security Number or ITIN
account_approval_letterApproval letter for account opening
cip_resultCustomer Identification Program result document
w8benIRS Form W-8BEN (Certificate of Foreign Status) — submitted as content_data JSON, not Base64
w9IRS Form W-9 (Request for Taxpayer Identification)
company_formationCertificate of incorporation or equivalent
entity_operating_documentOperating agreement or bylaws (for entity accounts)
entity_registrationState registration filing
hio_declaration_formHousehold/Immediate Office declaration form
limited_trading_authorizationAuthorization for a third party to trade on the account
pep_declaration_formPolitically Exposed Person declaration
Response
Returns 204 No Content on success. No body is returned.

Example requests

Identity verification — passport (image)

# First, Base64-encode your file
ENCODED=$(base64 -i passport_scan.png)

curl -X POST https://api.tappengine.com/v1/accounts/b3d9f1a2-4c7e-4f0b-9e2d-1a2b3c4d5e6f/documents/upload \
  -H "Authorization: Bearer $BUILDMARKETS_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"document_type\": \"identity_verification\",
    \"document_sub_type\": \"passport\",
    \"content\": \"$ENCODED\",
    \"mime_type\": \"image/png\"
  }"

Address verification — utility bill (PDF)

ENCODED=$(base64 -i utility_bill.pdf)

curl -X POST https://api.tappengine.com/v1/accounts/b3d9f1a2-4c7e-4f0b-9e2d-1a2b3c4d5e6f/documents/upload \
  -H "Authorization: Bearer $BUILDMARKETS_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"document_type\": \"address_verification\",
    \"document_sub_type\": \"utility_bill\",
    \"content\": \"$ENCODED\",
    \"mime_type\": \"application/pdf\"
  }"

W-8BEN — JSON form data

The w8ben document type accepts form data as a JSON object in content_data rather than a Base64-encoded file. The structure mirrors the IRS W-8BEN form fields.
curl -X POST https://api.tappengine.com/v1/accounts/b3d9f1a2-4c7e-4f0b-9e2d-1a2b3c4d5e6f/documents/upload \
  -H "Authorization: Bearer $BUILDMARKETS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "document_type": "w8ben",
    "content_data": {
      "name": "Maria García",
      "country_of_citizenship": "Mexico",
      "permanent_address": "123 Calle Principal, Ciudad de México, MX 06600",
      "mailing_address": "123 Calle Principal, Ciudad de México, MX 06600",
      "date_of_birth": "1985-04-12",
      "foreign_tax_id": "GARM850412XXX",
      "treaty_country": "Mexico",
      "treaty_article": "10",
      "withholding_rate": "10",
      "income_type": "Dividends",
      "signature_date": "2026-03-24"
    }
  }'

Response

HTTP/1.1 204 No Content

File size and format limits

ConstraintLimit
Maximum file size10MB per upload
Supported image typesimage/png, image/jpeg, image/gif, image/webp
Supported document typesapplication/pdf
EncodingBase64 (standard encoding, no line breaks required)
Tip: If you’re encoding in JavaScript, use Buffer.from(fileBuffer).toString('base64'). In Python, use base64.b64encode(file_bytes).decode('utf-8').

Common errors

HTTP StatusErrorCause
400INVALID_DOCUMENT_TYPEdocument_type is not one of the supported values
400CONTENT_REQUIREDcontent is missing for a non-w8ben document type
400CONTENT_DATA_REQUIREDcontent_data is missing for the w8ben document type
400FILE_TOO_LARGEDecoded file exceeds the 10MB size limit
400INVALID_MIME_TYPEThe mime_type is not supported
404ACCOUNT_NOT_FOUNDNo account found for the provided accountId

Next steps

Updated 3 months ago