Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Open and manage brokerage accounts via the Alpaca Broker API — account creation, KYC/CIP, identity & disclosures, agreements, document upload (incl. W-8BEN), account status lifecycle, and account updates/closure. Use when a developer is building onboarding, KYC, or account-management flows on Alpaca in any language.
.claude/skills/alpacahq-alpaca-broker-account-onboarding/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 48% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 64% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 75% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 79% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 6% | 0% |
Create and manage end-user brokerage accounts under your firm. This is the first step of any Broker API integration: no funding, journaling, or trading can happen until an account reaches ACTIVE.
> Read alpaca-broker-integration first for base URLs, auth, and conventions. This skill assumes Broker API + HTTP Basic auth.
https://docs.alpaca.markets/docs/getting-started-with-broker-api, https://docs.alpaca.markets/docs/accountshttps://docs.alpaca.markets/reference/createaccountalpaca-docs MCP → get-endpoint title "Broker API" path /v1/accounts| Method | Path | Purpose | |--------|------|---------| | POST | /v1/accounts | Create account (submit application) | | GET | /v1/accounts | List/query accounts (returns up to 1000) | | GET | /v1/accounts/{account_id} | Get one account (AccountExtended) | | PATCH | /v1/accounts/{account_id} | Update account | | POST | /v1/accounts/{account_id}/actions/close | Close account (returns 204) | | POST | /v1/accounts/{account_id}/documents/upload | Upload owner/KYC documents (array body) | | GET | /v1/accounts/{account_id}/documents | List uploaded documents | | POST / GET | /v1/accounts/{account_id}/cip | Submit / retrieve CIP results | | GET | /v1/country-info | Supported-country data | | GET | /v1/events/accounts/status | SSE stream of account-status changes → see alpaca-broker-sse-events |
POST /v1/accounts)Four objects are required: contact, identity, disclosures, agreements. documents and trusted_contact are optional but usually needed for KYC.
json{ "contact": { "email_address": "jane@example.com", "phone_number": "+15555555555", "street_address": ["20 N San Mateo Dr"], "city": "San Mateo", "state": "CA", // required if country / country_of_tax_residence is USA "postal_code": "94401", "country": "USA" // ISO 3166-1 alpha-3 }, "identity": { "given_name": "Jane", "family_name": "Doe", "date_of_birth": "1990-01-01", "tax_id_type": "USA_SSN", // see enum below "tax_id": "666-55-4321", "country_of_tax_residence": "USA", "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": "2026-01-02T18:09:33Z", "ip_address": "185.13.21.99" } ], "documents": [ /* OwnerDocumentUploadRequest[] — see §4 */ ], "trusted_contact": { "given_name": "Jim", "family_name": "Doe", "email_address": "jim@example.com" } }
Key field rules:
contact.street_address is an array (max 3 lines). contact.state required when country/tax-residence is USA.identity.funding_source is an array; one+ of employment_income, investments, inheritance, business_income, savings, family.tax_id_type enum is large and country-specific: USA_SSN, USA_ITIN, IND_PAN, MEX_RFC, GBR_NINO, … plus generic NATIONAL_ID, PASSPORT, DRIVER_LICENSE, OTHER_GOV_ID, NOT_SPECIFIED. Query the spec for the full list rather than hardcoding.account_type (trading|custodial|donor_advised|ira), account_sub_type (IRA: traditional|roth), enabled_assets (us_equity|us_option|crypto|ipo, default us_equity).investment_objective/investment_time_horizon/liquidity_needs/risk_tolerance moved from identity to top-level.Responses: 200 → account object · 409 email already registered · 422 invalid value · 400 malformed body.
Each entry: agreement (customer_agreement, account_agreement, margin_agreement, crypto_agreement, options_agreement), signed_at (RFC3339), ip_address (IPv4), optional revision. You must present the agreement text to the user and capture the real signing timestamp + IP — Alpaca treats these as the legal record. revision defaults to the currently-active revision if omitted.
documents[] items: document_type + (content base64 or content_data).
json{ "document_type": "identity_verification", "content": "<base64>", "mime_type": "image/jpeg", "document_sub_type": "passport" }
document_type enum includes identity_verification, address_verification, date_of_birth_verification, tax_id_verification, w8ben, w9, cip_result, and more.mime_type: application/pdf, image/png, image/jpeg — plus application/json only for w8ben.content_data as a structured W8benDocument JSON object (full_name, country_citizen, permanent_address_, date_of_birth, ip_address, timestamp, signer_full_name, …) and Alpaca renders the official form for you. This is the clean way to satisfy the tax-form requirement for non-US persons programmatically.status (and crypto_status) use the AccountStatus enum:
| Status | Meaning | |--------|---------| | ONBOARDING | Application expected, not yet submitted | | SUBMITTED | Submitted, being processed | | SUBMISSION_FAILED | Submission error | | ACTION_REQUIRED | Needs manual action (e.g. a true disclosure routes here) | | APPROVAL_PENDING | Approval in progress (documented "initial value") | | APPROVED | Approved, waiting to go active | | ACTIVE | Fully usable — funding & trading allowed | | REJECTED | Application rejected | | ACCOUNT_UPDATED | Modified by user | | ACCOUNT_CLOSED | Closed | | INACTIVE | Not enabled for the given asset |
Happy path: SUBMITTED → APPROVAL_PENDING → APPROVED → ACTIVE.
Lesson — gate every downstream op on ACTIVE. A 200 from POST /v1/accounts does not mean tradable. Subscribe to account-status SSE events (or poll GET /v1/accounts/{id}) and only enable funding/journals/trading once status == ACTIVE. Trying to journal or trade into a non-active account fails.
kyc_results on the account object carries reject/accept/indeterminate categories (KYCResultType values like IDENTITY_VERIFICATION, TAX_IDENTIFICATION, ADDRESS_VERIFICATION, WATCHLIST_HIT, COUNTRY_NOT_SUPPORTED, OTHER) plus additional_information. summary is pass/fail (internal only).POST /v1/accounts/{id}/cip with a CIPInfo body (provider_name, kyc, document, photo, identity, watchlist sub-results). Sub-check results are clear/consider.WATCHLIST_HIT / COUNTRY_NOT_SUPPORTED require no user action — Alpaca handles them manually.USA, PHL, …) — strip any UI decoration (flags/emoji, display names) and validate against GET /v1/country-info. Garbage in country/country_of_tax_residence is a common 422.signed_at and ip_address must reflect the actual user action, not server time / a placeholder.account_id immediately, then drive UI off the status events, not the create response.409 on duplicate email is your friend — look up the existing account rather than retrying creation. Store your local user↔account_id mapping before the network call so a timeout doesn't orphan an account.POST .../actions/close, you must liquidate all positions and withdraw all cash. The account record is not deleted — it goes ACCOUNT_CLOSED.Related skills: fund the account → alpaca-broker-funding-transfers; move cash in via the firm sweep → alpaca-broker-journals; trade → alpaca-broker-trading-orders; track status in real time → alpaca-broker-sse-events.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-04 | pass→pass | 5,260 | 2,416 | -54% | 1 | 1 | 0% | 1,082 | 2,907 | +169% | 0 | 0 | — |
case-01 | fail→pass | 15,368 | 9,189 | -40% | 1 | 1 | 0% | 3,230 | 4,787 | +48% | 0 | 0 | — |
case-02 | fail→pass | 15,814 | 11,671 | -26% | 1 | 1 | 0% | 2,880 | 4,720 | +64% | 0 | 0 | — |
case-03 | fail→pass | 10,226 | 5,194 | -49% | 1 | 1 | 0% | 2,059 | 3,605 | +75% | 0 | 0 | — |
case-05 | pass→pass | 12,271 | 7,853 | -36% | 1 | 1 | 0% | 2,544 | 4,250 | +67% | 0 | 0 | — |
case-06 | pass→pass | 8,384 | 3,183 | -62% | 1 | 1 | 0% | 1,623 | 3,154 | +94% | 0 | 0 | — |
case-07 | pass→pass | 4,163 | 2,560 | -39% | 1 | 1 | 0% | 744 | 3,095 | +316% | 0 | 0 | — |
case-08 | pass→pass | 7,171 | 3,320 | -54% | 1 | 1 | 0% | 1,461 | 3,102 | +112% | 0 | 0 | — |
case-09 | pass→pass | 10,979 | 5,358 | -51% | 1 | 1 | 0% | 2,131 | 3,541 | +66% | 0 | 0 | — |
case-10 | fail→pass | 12,724 | 3,828 | -70% | 1 | 1 | 0% | 1,855 | 3,312 | +79% | 0 | 0 | — |
case-11 | fail→pass | 12,352 | 2,093 | -83% | 1 | 1 | 0% | 2,803 | 2,961 | +6% | 0 | 0 | — |
case-12 | pass→pass | 12,692 | 3,774 | -70% | 1 | 1 | 0% | 2,143 | 3,158 | +47% | 0 | 0 | — |
case-13 | pass→pass | 10,609 | 5,110 | -52% | 1 | 1 | 0% | 1,976 | 3,556 | +80% | 0 | 0 | — |
case-14 | pass→pass | 3,811 | 1,436 | -62% | 1 | 1 | 0% | 653 | 2,801 | +329% | 0 | 0 | — |
case-15 | fail→pass | 5,932 | 2,116 | -64% | 1 | 1 | 0% | 1,017 | 2,899 | +185% | 0 | 0 | — |
case-16 | pass→pass | 5,305 | 2,374 | -55% | 1 | 1 | 0% | 1,086 | 2,990 | +175% | 0 | 0 | — |
case-17 | fail→pass | 6,524 | 2,667 | -59% | 1 | 1 | 0% | 1,188 | 2,997 | +152% | 0 | 0 | — |
case-18 | fail→pass | 8,937 | 2,404 | -73% | 1 | 1 | 0% | 1,903 | 2,900 | +52% | 0 | 0 | — |
case-19 | pass→pass | 3,146 | 1,442 | -54% | 1 | 1 | 0% | 564 | 2,794 | +395% | 0 | 0 | — |
case-20 | fail→fail | 13,428 | 12,549 | -7% | 1 | 1 | 0% | 2,993 | 4,355 | +46% | 0 | 0 | — |
case-21 | fail→fail | 7,449 | 3,673 | -51% | 1 | 1 | 0% | 1,454 | 3,337 | +130% | 0 | 0 | — |
case-22 | fail→fail | 5,719 | 5,462 | -4% | 1 | 1 | 0% | 1,221 | 3,658 | +200% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +36 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.