EVE FINANCE Logo

EVE FINANCE

Eve Broker API Reference

Complete developer documentation for the Eve Broker API

Base URL: broker-api.eve.markets
Sandbox: broker-api.sandbox.eve.markets
Auth: authx.eve.markets
1. Architecture Overview

The Eve Broker API lets you build a complete trading platform for end users in the capacity of a broker-dealer.

CapabilityYouEve
🎨 Frontend ExperienceUI / UX / App / Web
📝 Customer OnboardingCollect & submit KYCReview
💰 Fund CustodySIPC protection ($500K)
📈 Order RoutingMulti-venue routing
📜 Regulatory ComplianceFINRA member
🧾 Clearing & SettlementApex Clearing
📊 ReportingAuto statements / confirms

Three Partnership Models

ModelAccount StructureUse Case
Fully-DisclosedIndividual account per customerStandard broker app
OmnibusOne master account, you keep internal booksHigh-frequency / institutional
RIARegistered Investment AdvisorAdvisory services
2. Authentication

Endpoint Matrix

EnvironmentBroker APIMarket DataAuth
Livebroker-api.eve.marketsdata.eve.marketsauthx.eve.markets
Sandboxbroker-api.sandbox.eve.marketsdata.sandbox.eve.marketsauthx.sandbox.eve.markets

Client Credentials (Recommended)

Step 1: Obtain an access token (valid for 15 minutes)

Bash

curl -X POST "https://authx.sandbox.eve.markets/v1/oauth2/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET"

Response

{"access_token": "eyJ...", "expires_in": 899, "token_type": "Bearer"}

Step 2: Include the token in all requests

curl -H "Authorization: Bearer eyJ..." \
  "https://broker-api.sandbox.eve.markets/v1/accounts"

Reuse tokens: Do not re-request within 15 minutes. Supports both client_secret_post and private_key_jwt (RFC 7523).

Legacy Authentication

curl -H "APCA-API-KEY-ID: xxx" \
  -H "APCA-API-SECRET-KEY: xxx" \
  "https://api.eve.markets/v2/account"

Idempotency

Creation requests support the Idempotency-Key header:

  • Same key + same body → returns existing result (no duplication)
  • Same key + different body → 422 error
  • Recommended format: UUID
3. Account Onboarding

Complete Flow

User fills form → POST /v1/accounts → Eve KYC review

Onfido identity verification → Upload result → CIP submission

NEW → SUBMITTED → PENDING → APPROVED → ACTIVE

Core API Endpoints

OperationEndpointDescription
Create accountPOST /v1/accountsSubmit contact + identity + disclosures
List accountsGET /v1/accountsMax 1000, paginate by created_after/before
Get accountGET /v1/accounts/{id}Includes documents attribute
Update accountPATCH /v1/accounts/{id}Modify information
Close accountPOST /v1/accounts/{id}/actions/closeMust liquidate & withdraw first
Upload documentPOST /v1/accounts/{id}/documents/uploadPDF/JPG/PNG
Get CIPGET /v1/accounts/{id}/cipCustomer Identification Program
Submit CIPPOST /v1/accounts/{id}/cipSubmit KYC verification result
Onfido tokenGET /v1/accounts/{id}/onfido-sdk-tokensGet SDK token
Onfido resultPATCH /v1/accounts/{id}/onfido-sdkUpload SDK verification result

Account Creation Request Body (Fully-Disclosed)

JSON

{
  "contact": {
    "email_address": "john@example.com",
    "phone_number": "7065912538",
    "street_address": ["20 S Craig Ave"],
    "city": "San Mateo",
    "state": "CA",
    "postal_code": "33345"
  },
  "identity": {
    "given_name": "John",
    "family_name": "Doe",
    "date_of_birth": "1990-01-01",
    "tax_id_type": "USA_SSN",
    "tax_id": "661010666",
    "country_of_citizenship": "USA",
    "country_of_birth": "USA",
    "country_of_tax_residence": "USA",
    "funding_source": ["employment_income"],
    "annual_income_min": "50000",
    "annual_income_max": "100000",
    "total_net_worth_min": "100000",
    "total_net_worth_max": "500000",
    "liquid_net_worth_min": "50000",
    "liquid_net_worth_max": "250000",
    "liquidity_needs": "does_not_apply",
    "investment_experience": "limited",
    "risk_tolerance": "moderate",
    "investment_objective": "growth",
    "employment_status": "employed",
    "employer_name": "Acme Corp",
    "visa_type": "not_applicable"
  },
  "disclosures": {
    "is_control_person": false,
    "is_affiliated_exchange_or_finra": false,
    "is_politically_exposed": false,
    "is_immediate_family_exposed": false,
    "employment_status": "employed"
  },
  "agreements": [
    {
      "agreement": "customer_agreement",
      "signed_at": "2024-01-01T00:00:00Z",
      "ip_address": "127.0.0.1"
    },
    {
      "agreement": "options_agreement",
      "signed_at": "2024-01-01T00:00:00Z",
      "ip_address": "127.0.0.1"
    }
  ],
  "trusted_contact": {
    "given_name": "Jane",
    "family_name": "Doe",
    "email_address": "jane@example.com"
  }
}

Multi-Account (MLA)

Existing users can open sub-accounts by passing primary_account_holder_id:

JSON

{
  "primary_account_holder_id": "existing-account-uuid",
  "account_type": "ira",
  "minor_type": "traditional_ira"
}

Onfido Identity Verification

  1. Get SDK Token: GET /v1/accounts/{id}/onfido-sdk-tokens{"sdk_token": "..."}
  2. Frontend uses token to launch Onfido SDK (user selfie + ID scan)
  3. After verification, upload result: PATCH /v1/accounts/{id}/onfido-sdk (Onfido callback auto-notifies Eve)

CIP Minimum Required Fields

POST /v1/accounts/{'{id}'}/cip

{
  "tax_id_type": "USA_SSN",
  "tax_id": "661010666",
  "date_of_birth": "1990-01-01"
}

File Upload

Supports PDF, JPG, PNG:

curl -X POST "https://broker-api.sandbox.eve.markets/v1/accounts/{id}/documents/upload" \
  -H "Authorization: Bearer ..." \
  -F "file=@driver_license.jpg" \
  -F "document_type=id_card" \
  -F "document_sub_type=drivers_license"
4. KYC & Compliance

Who is Responsible for KYC?

KYC responsibility differs based on your partnership model:

ModelKYC OwnerPost-Onboarding Status
Trading App / RIAEveSUBMITTED → Eve auto-review
Fully-Disclosed BrokerYouAPPROVED (directly after your review)
OmnibusNo individual onboarding

Model 1: Trading App / RIA — Eve Reviews

You collect info → Eve reviews → You wait for results

① POST /v1/accounts
├─ contact (contact info)
├─ identity (identity + financial info)
├─ disclosures (compliance declarations)
└─ agreements (signed agreements)

② Account → SUBMITTED → Eve auto KYC
├─ Blacklist screening
├─ SSN verification
├─ Address verification
└─ OFAC / sanctions list check

③ Result (SSE push):
├─ APPROVED → ACTIVE ✅
├─ APPROVAL_PENDING → Eve manual review ⏳
├─ ACTION_REQUIRED → supplementary docs 📎
└─ REJECTED ❌

Upload Supplementary Documents

curl -X POST "https://broker-api.sandbox.eve.markets/v1/accounts/{id}/documents/upload" \
  -H "Authorization: Bearer ***" \
  -F "file=@utility_bill.pdf" \
  -F "document_type=proof_of_address" \
  -F "document_sub_type=utility_bill"

Common ACTION_REQUIRED reasons:

  • Address unverified → Upload utility bill / bank statement
  • Identity unconfirmed → Upload driver's license / passport
  • SSN mismatch → Upload SSN card

Model 2: Fully-Disclosed Broker — You Review

You complete KYC yourself → Tell Eve the result → Immediate account opening

  1. You perform your own KYC (using your vendor: Jumio / Plaid IDV / LexisNexis etc.)
  2. POST /v1/accounts → status directly APPROVED (since you already verified)
  3. Eve only runs blacklist screening (match → REJECTED / APPROVAL_PENDING)
  4. POST /v1/accounts/{id}/cip ← Submit your KYC result

CIP Minimum Required Fields (FINRA)

FieldDescription
NameFull name
Date of BirthDate of birth
AddressResidential address
Tax ID (SSN/TIN)Taxpayer identification number

Onfido Identity Verification Flow (Recommended for Trading App)

Your Frontend → Your Backend → Eve
① User fills form
② POST /v1/accounts
③ GET /v1/accounts/{id}/onfido-sdk-tokens → returns sdk_token
④ Return sdk_token to frontend
⑤ Launch Onfido SDK — user photo + selfie, Onfido auto-verifies
⑥ Verification complete (callback)
⑦ PATCH /v1/accounts/{id}/onfido-sdk
⑧ Eve receives result, continues KYC review

Key Implementation Points

1. Get SDK Token:

GET /v1/accounts/{account_id}/onfido-sdk-tokens
→ {"sdk_token": "eyJ..."}

2. Frontend Integration (onfido-sdk-ui npm package):

JavaScript

import { Onfido } from 'onfido-sdk-ui';

Onfido.init({
  token: sdkToken,
  containerId: 'onfido-mount',
  onComplete: (data) => {
    fetch('/api/onfido-result', {
      method: 'PATCH',
      body: JSON.stringify({ account_id, result: data })
    });
  }
});

3. Upload Onfido Result:

PATCH /v1/accounts/{account_id}/onfido-sdk
# Body can be empty (Onfido auto-notifies Eve)

International Clients — W-8BEN

Non-US tax residents (W-9 not applicable) must submit W-8BEN:

POST /v1/accounts/{id}/documents/upload \
  -F "file=@w8ben.pdf" \
  -F "document_type=w8ben"

W-8BEN must be renewed every 3 years (signing year + 3 years).

KYC Status Enumeration

StatusMeaningYour Action
ONBOARDINGJust created, KYC not startedWait or trigger Onfido
SUBMITTEDSubmitted for reviewWait for Eve
APPROVAL_PENDINGKYC not auto-passed, manual reviewWait
ACTION_REQUIREDSupplementary materials neededUpload documents
APPROVEDApprovedCan deposit & trade
ACTIVEActivatedAll features available
REJECTEDRejectedContact user / Eve
SUBMISSION_FAILEDSystem failureEve auto-handles

Practical Minimal KYC Pipeline

Python (pseudocode)

class EveKYC:
    def onboard(self, user_data):
        # 1. Create account
        account = self.api.post('/v1/accounts', {
            'contact': user_data.contact,
            'identity': {
                'given_name': user_data.first_name,
                'family_name': user_data.last_name,
                'date_of_birth': user_data.dob,  # "1990-01-01"
                'tax_id_type': 'USA_SSN',
                'tax_id': user_data.ssn,  # "661010666"
                'funding_source': ['employment_income'],
                'annual_income_min': user_data.income_range,
            },
            'disclosures': {},
            'agreements': [
                {'agreement': 'customer_agreement', 'signed_at': now, 'ip_address': user_ip}
            ]
        })
        account_id = account['id']

        # 2. Trigger Onfido
        sdk_token = self.api.get(f'/v1/accounts/{account_id}/onfido-sdk-tokens')
        return account_id, sdk_token['sdk_token']

    def on_onfido_complete(self, account_id):
        # 3. Notify Eve after Onfido completes
        self.api.patch(f'/v1/accounts/{account_id}/onfido-sdk')
        # 4. If Fully-Disclosed, submit CIP
        self.api.post(f'/v1/accounts/{account_id}/cip', {
            'tax_id_type': 'USA_SSN',
            'tax_id': ssn,
            'date_of_birth': dob
        })

    def check_status(self, account_id):
        # 5. Poll or listen via SSE for status changes
        acct = self.api.get(f'/v1/accounts/{account_id}')
        return acct['status']  # SUBMITTED / ACTION_REQUIRED / APPROVED / ACTIVE

Key Pitfalls

Strict SSN format validation — do not submit numbers like 123456789 that look like test data.

approved_level and margin_multiplier — cannot be set at onboarding; only via PATCH after account is ACTIVATED.

W-8BEN date format — the form uses MM-DD-YYYY, but API submission must use YYYY-MM-DD.

Onfido token in memory only — do not persist; discard after use.

SSE events may duplicate — deduplicate using the at field.

Production rate limits — do not send concurrent onboarding request bursts.

5. Account Status & Lifecycle
StatusMeaningActions Available
NEWJust submittedEdit, upload documents
SUBMITTEDSubmitted for reviewWait
PENDINGUnder review
ACTION_REQUIREDSupplementary materials neededUpload docs / Onfido
APPROVEDApprovedDeposit, trade
ACTIVEActivatedAll
DISABLEDDisabledContact Eve
CLOSEDClosedIrreversible
REJECTEDRejectedContact Eve
SUSPENDEDSuspendedContact Eve

Status Flow

NEW → SUBMITTED → PENDING ──→ APPROVED → ACTIVE
↓ ↓ ↓ ↓
REJECTED ACTION_ CLOSED DISABLED / SUSPENDED
REQUIRED

APPROVED

Account Attributes

AttributeDescription
account_typetrading / ira
minor_typetraditional_ira / roth_ira / sep_ira
marginMargin account enabled
options_approved_level0 - 4
crypto_statusCrypto trading enabled
currencyUSD / multi-currency (LCT)
system_day_trades_leftRemaining day trades
6. Data Validation

All data must pass FINRA CAIS validation, otherwise a 422 is returned.

Name / Address Romanization

given_name, family_name, street_address, city, state etc. must be in ASCII range 32-126. To preserve the original script, use local_* fields (e.g. local_given_name).

SSN Validation Rules

661010666(9 digits)
000123456(Area Number cannot be 000)
666123456(Area Number cannot be 666)
123004567(Group Number cannot be 00)
1234560000(Serial Number cannot be 0000)
111111111(cannot be all same digits)
123456789(cannot be sequential ascending)
987654321(cannot be sequential descending)

Funding Source Verification

JSON

"funding_source": ["employment_income"]  // at least one required
"annual_income_min": "50000_99999"        // annual income range

Age Restriction

Must be ≥ 18 years old.

funding_source Options

employment_income
investments
inheritance
business_income
savings
family
pension
real_estate
alimony
disability
social_security

Financial Range Enumerations

RangeEnum Value
< $10K0_9999
$10K - $25K10000_24999
$25K - $50K25000_49999
$50K - $100K50000_99999
$100K - $250K100000_249999
$250K - $500K250000_499999
> $500K500000_up

Agreement Types

customer_agreement
margin_agreement
options_agreement
crypto_agreement
ach_agreement
ira_agreement
account_transfer_agreement
esign_agreement
privacy_policy
data_sharing_disclosure
wire_agreement
sweep_agreement
7. Funding & Transfers

Fund Flow

User Bank → Plaid Link → ACH Relationship → Transfer API → Credited

Wire Transfer → ↑
Instant Funding → ↑
Journals → ↑

7.1 Sandbox Testing

In sandbox, all transfers are instant:

POST /v1/accounts/{'{id}'}/transfers

{
  "transfer_type": "ach",
  "relationship_id": "rel-uuid",
  "direction": "INCOMING",
  "amount": "5000.00"
}

→ Account immediately available

7.2 ACH Deposits & Withdrawals

Step 1: Plaid Link — user authorizes in-app → obtain processor_token

Step 2: Create ACH Relationship

POST /v1/accounts/{'{id}'}/ach-relationships

{
  "processor_token": "plaid-processor-token",
  "account_owner_name": "John Doe"
}

Status: PENDING → APPROVED

Step 3: Initiate Transfer

POST /v1/accounts/{'{id}'}/transfers

{
  "transfer_type": "ach",
  "relationship_id": "rel-uuid",
  "direction": "INCOMING",
  "amount": "1000.00"
}

Status: QUEUED → APPROVED → PROCESSING → COMPLETE

7.3 Wire Transfers

First create a bank object:

POST /v1/accounts/{'{id}'}/recipient-banks

{
  "name": "Chase",
  "bank_code_type": "ABA",
  "bank_code": "021000021",
  "account_number": "123456789"
}

Then transfer:

POST /v1/accounts/{'{id}'}/transfers

{
  "transfer_type": "wire",
  "bank_id": "bank-uuid",
  "direction": "OUTGOING",
  "amount": "5000.00",
  "fee_payment_method": "user"
}

Incoming wires require FFC instructions: FFC: {correspondent_name} {account_number}

7.4 International Wire (SWIFT)

POST /v1/accounts/{'{id}'}/recipient-banks

{
  "name": "HSBC Hong Kong",
  "bank_code_type": "SWIFT",
  "bank_code": "HSBCHKHH",
  "account_number": "000123456",
  "bank_country_code": "HKG",
  "bank_city": "Hong Kong",
  "international_bank_name": "HSBC"
}

7.5 Journals

Internal transfers between accounts:

POST /v1/journals

{
  "from_account": "account-id-A",
  "to_account": "account-id-B",
  "entry_type": "JNLC",
  "amount": "1000.00"
}

entry_type: JNLC (cash) or JNLS (security)

8. Trading System

Order Submission

POST /v2/orders

{
  "symbol": "AAPL",
  "qty": 1,
  "side": "buy",
  "type": "market",
  "time_in_force": "day",
  "client_order_id": "my-order-001"
}

Order Types

TypeDescription
marketExecute at best available price
limitExecute at specified price or better
stopStop order (triggers market order)
stop_limitStop-limit order
trailing_stopTrailing stop order

Time in Force (TIF)

day
gtc
opg
cls
ioc
fok

Order Lifecycle

new → partially_filled → filled
↓ ↓
rejected canceled

expired

Position Management

GET /v2/positions              # List all positions
GET /v2/positions/{symbol}    # Get specific position
DELETE /v2/positions/{symbol} # Close position

Market Data

GET /v2/stocks/{symbol}/quotes/latest   # Latest quote
GET /v2/stocks/{symbol}/bars            # Historical bars
GET /v2/stocks/{symbol}/trades/latest   # Latest trades
8. Real-Time Events (SSE)

Server-Sent Events stream real-time updates for account status changes, trade executions, and transfers.

JavaScript

const eventSource = new EventSource(
  'https://broker-api.sandbox.eve.markets/v1/events?token=YOUR_TOKEN'
);

eventSource.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log('Event:', data);
  // Deduplicate using data.at field
};

Event Types

EventDescription
accounts.updatedAccount status changed
trades.executedOrder filled
transfers.completedTransfer completed
journals.processedJournal processed
non_trading_dayMarket holiday notification

SSE events may duplicate — always deduplicate using the at timestamp field.

9. Portfolio Rebalancing

Automated portfolio rebalancing lets you define target allocations and execute trades to maintain them.

POST /v1/rebalancing/runs

{
  "account_id": "account-uuid",
  "portfolio_type": "predefined",
  "portfolio_name": "Tech Focus",
  "allocation": {
    "AAPL": 0.40,
    "MSFT": 0.30,
    "GOOGL": 0.30
  }
}

The API calculates required trades and executes them automatically.

10. IPO Subscriptions

Participate in Initial Public Offerings for eligible accounts.

POST /v1/ipo/subscriptions

{
  "account_id": "account-uuid",
  "ipo_id": "ipo-uuid",
  "shares": 100,
  "price_limit": "50.00"
}

Status flow: PENDING → ALLOCATED → CONFIRMED

11. Fixed Income

Trade Treasury bonds, corporate bonds, and other fixed-income securities.

GET /v1/treasuries          # List available treasuries
POST /v1/orders            # Submit bond order with CUSIP
12. End-of-Day & Reconciliation

Daily reconciliation processes run at market close to ensure consistency between Eve and Apex Clearing.

  • Position reconciliation
  • Cash balance reconciliation
  • Trade settlement confirmation
  • Corporate action processing
  • Monthly statements auto-generated
GET /v1/account/portfolio/{account_id}  # End-of-day snapshot
GET /v1/statements                     # List statements
13. 24/5 Extended Trading

Eve supports extended-hours trading for 5 days a week, enabling pre-market and after-hours order execution.

Pre-Market

4:00 - 9:30 AM ET

Regular Hours

9:30 AM - 4:00 PM ET

After Hours

4:00 - 8:00 PM ET

14. Managed Accounts

Managed (custody) accounts allow advisors to trade on behalf of clients.

POST /v1/accounts

{
  "account_type": "trading",
  "managed": true,
  "manager_id": "advisor-uuid"
}
15. ACAT Transfers

Automated Customer Account Transfer (ACAT) for moving assets between brokerages.

POST /v1/acat/transfers

{
  "account_id": "account-uuid",
  "transfer_type": "FULL",
  "contra_broker": "DTC#",
  "contra_account_number": "12345678"
}

Status: PENDING → IN_PROGRESS → COMPLETED

16. Integration Guide

Step-by-Step Integration

  1. Register — Contact Eve to obtain client_id and client_secret
  2. Sandbox testing — Use sandbox environment for all development
  3. Implement OAuth2 — Client Credentials flow, cache tokens for 15 min
  4. Build onboarding flow — Account creation + KYC + Onfido
  5. Integrate Plaid — Bank linking for ACH transfers
  6. Implement SSE listener — Real-time event handling
  7. Go live — Switch endpoints to production, test with small amounts

Recommended Tech Stack

Backend

Node.js / Python / Go

Frontend

React / Vue / Angular

KYC Vendor

Onfido / Jumio / Plaid IDV

Bank Linking

Plaid

17. FAQ

Q: How long do access tokens last?

A: 15 minutes. Cache and reuse until expiry.

Q: What is the difference between the three partnership models?

A: Fully-Disclosed = individual accounts per customer; Omnibus = one master account with your internal books; RIA = for registered investment advisors.

Q: Who handles KYC?

A: In Trading App / RIA mode, Eve handles KYC. In Fully-Disclosed mode, you handle KYC and submit results via CIP endpoint.

Q: How do I handle ACTION_REQUIRED status?

A: Upload the requested supplementary documents (utility bill, ID, SSN card) via the document upload endpoint.

Q: Are transfers instant in production?

A: No — only in sandbox. In production, ACH transfers take 1-3 business days; wires are typically same-day.

Q: How often must W-8BEN be renewed?

A: Every 3 years (signing year + 3 years).

© 2004 EVE FINANCE. All rights reserved. Eve Broker API Documentation.