Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Move cash (JNLC) and securities (JNLS) BETWEEN accounts inside your own Alpaca omnibus via the Broker API — single, batch, and reverse-batch journals, the Idempotency-Key header, journal status lifecycle including corrections, and the firm/sweep-account pattern that powers instant funding and share rewards. Use for internal account-to-account movement in any language. For deposits/withdrawals to EXTERNAL banks, use funding-transfers instead.
.claude/skills/alpacahq-alpaca-broker-journals/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-06 | ✗→✓ | ▲ Improved | 97% | 0% |
| case-01 | ✗→✓ | ▲ Improved | 50% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 12% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 34% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 84% | 0% |
Journals move value between two accounts within your own Alpaca omnibus — typically between a pre-funded firm/sweep account and a user account. They are the engine behind "instant funding," cashback, and share rewards. They never touch the outside banking world (that's alpaca-broker-funding-transfers).
> Read alpaca-broker-integration first. Broker API + HTTP Basic auth.
https://docs.alpaca.markets/docs/funding-via-journalshttps://docs.alpaca.markets/reference/createjournalalpaca-docs MCP → get-endpoint title "Broker API" path /v1/journals| Method | Path | Purpose | |--------|------|---------| | POST | /v1/journals | Single journal (JNLC cash or JNLS shares) | | POST | /v1/journals/batch | One source → many destinations (JNLC only) | | POST | /v1/journals/reverse_batch | Many sources → one destination (JNLC only) | | GET | /v1/journals | List (filters: after, before, status, entry_type, to_account, from_account, limit) | | GET | /v1/journals/{journal_id} | Retrieve one | | DELETE | /v1/journals/{journal_id} | Cancel a pending journal (204) | | GET | /v2/events/journals/status | SSE journal status stream (v1 is legacy) |
entry_type is exactly "JNLC" or "JNLS".
JNLC — cash. Moves USD between accounts. Allowed firm ↔ user, both directions. Not customer-to-customer.JNLS — securities. Moves whole/fractional shares. Allowed firm → user only. Used for signup/referral share rewards.json// JNLC (cash) { "entry_type": "JNLC", "from_account": "<firm-uuid>", "to_account": "<user-uuid>", "amount": "100.00" } // JNLS (shares) { "entry_type": "JNLS", "from_account": "<firm-uuid>", "to_account": "<user-uuid>", "symbol": "AAPL", "qty": "0.5" }
| Field | JNLC | JNLS | Notes | |-------|------|------|-------| | from_account / to_account | required | required | account UUIDs | | amount | required | — | decimal string | | symbol / qty | — | required | qty is a string; fractional allowed | | currency | optional | optional | defaults USD | | description | optional | optional | ≤1024 chars; accepts sandbox fixtures | | transmitter_* | optional (JNLC) | n/a | Travel Rule fields |
Responses: 200 journal · 403 amount/assets not available · 404 account not found · 422 idempotency-key reused with a different body.
Pass an Idempotency-Key header (≤128 chars; a client-generated UUID is recommended) on journal creates.
422.Lesson: this is the correct way to make money movement retry-safe. Without it, a network timeout on POST /v1/journals leaves you unsure whether the cash moved — and a blind retry can double-fund. Generate the key deterministically from your own transaction ID and send it on every attempt.
Batch — one-to-many (fan a sweep account out to many users):
json{ "entry_type": "JNLC", "from_account": "<firm-uuid>", "entries": [ { "to_account": "<u1>", "amount": "1000" }, { "to_account": "<u2>", "amount": "250" } ] }
Reverse batch — many-to-one (pull cash from many users back to the firm account):
json{ "entry_type": "JNLC", "to_account": "<firm-uuid>", "entries": [ { "from_account": "<u1>", "amount": "10" }, { "from_account": "<u2>", "amount": "100" } ] }
Every entry must validate or the entire batch fails (one bad account ID kills it). The response is an array of BatchJournalResponse (the Journal object + an error_message per entry that failed). Idempotency-Key is supported with the same semantics.
JournalStatus: queued, sent_to_clearing, pending, executed, rejected, canceled, refused, deleted, correct.
Happy path: queued → sent_to_clearing → executed.
| Status | Meaning | Terminal | |--------|---------|----------| | queued | In queue | no | | sent_to_clearing | Submitted to books-and-records | no | | pending | Needs Alpaca ops approval (e.g. hit a JNLC daily limit) | no | | executed | Balances updated — but NOT final, can still be reversed by cashiering | no (not final) | | rejected | Manually rejected | no | | refused | Failed preliminary checks; never hit the ledger (e.g. a fast replay failing the balance check) | no | | canceled | Canceled via API/ops | FINAL | | deleted | Removed from ledger | FINAL | | correct | A prior executed journal was cancelled and re-created with a corrected amount | FINAL |
Two critical lessons:
executed ≠ final. Don't treat executed as irreversible — Alpaca cashiering can reverse a journal that wasn't permitted. Reconcile against later events.correct creates a NEW journal ID. A correction cancels the original and issues a new journal with the corrected amount — it is not an in-place edit. If you reconcile by journal ID, the original ID transitions to correct/cancelled while a different ID carries the real funds. Handle both. (This is why event consumers must be idempotent and ID-keyed — see alpaca-broker-reconciliation-idempotency.)GET /v2/events/journals/status pushes JournalStatusEventV2: event_id (ULID, sortable), journal_id, entry_type, status_from, status_to, description, idempotency_key, idempotency_key_type (single|batch), batch_error_message. Replay rules: since required if until set; since_id required if until_id set; can't mix since with since_id. Without a since/since_id, no history is returned. See alpaca-broker-sse-events.
to_account must be ACTIVE; from_account must be ACTIVE or CLOSE.403 if the amount isn't available; reverse-batch 403 = insufficient balance/assets.pending (manual ops approval).GET /v1/journals returns 422 if the result set exceeds 100,000 records — always filter with after/before/limit.DELETE succeeds (204) only when pending; an executed journal → 422. To reverse an executed journal, create a mirror journal in the opposite direction, don't try to delete it.description (e.g. /fixtures/status=rejected/fixtures/) to simulate rejected/pending outcomes for testing.The canonical Broker API funding architecture:
Bulk external wire ──> FIRM / SWEEP account (pre-funded) ──JNLC──> user accounts (instant)
user account ──JNLC──> FIRM account ──external wire/ACH──> outside world (withdrawal)You collect money your own way, hold it in a firm account, and journal it to users instantly rather than running a per-user external transfer. Withdrawals reverse the flow. This is what makes "instant deposit" UX possible on top of slow banking rails.
Related skills: external money in/out → alpaca-broker-funding-transfers; retry-safety & corrections → alpaca-broker-reconciliation-idempotency; decimal handling → alpaca-broker-money-precision; events → alpaca-broker-sse-events.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-06 | fail→pass | 9,938 | 6,043 | -39% | 1 | 1 | 0% | 1,697 | 3,337 | +97% | 0 | 0 | — |
case-01 | fail→pass | 18,884 | 16,520 | -13% | 1 | 1 | 0% | 3,815 | 5,716 | +50% | 0 | 0 | — |
case-02 | fail→pass | 22,281 | 14,436 | -35% | 1 | 1 | 0% | 4,882 | 5,457 | +12% | 0 | 0 | — |
case-03 | fail→pass | 19,020 | 13,232 | -30% | 1 | 1 | 0% | 3,621 | 4,860 | +34% | 0 | 0 | — |
case-04 | pass→pass | 10,676 | 9,292 | -13% | 1 | 1 | 0% | 2,302 | 4,367 | +90% | 0 | 0 | — |
case-05 | fail→pass | 10,342 | 7,316 | -29% | 1 | 1 | 0% | 1,908 | 3,506 | +84% | 0 | 0 | — |
case-07 | fail→pass | 11,895 | 5,684 | -52% | 1 | 1 | 0% | 1,955 | 3,166 | +62% | 0 | 0 | — |
case-08 | fail→pass | 11,030 | 4,999 | -55% | 1 | 1 | 0% | 2,473 | 3,209 | +30% | 0 | 0 | — |
case-09 | fail→pass | 13,880 | 4,568 | -67% | 1 | 1 | 0% | 2,359 | 3,126 | +33% | 0 | 0 | — |
case-10 | pass→pass | 11,206 | 6,365 | -43% | 1 | 1 | 0% | 2,168 | 3,431 | +58% | 0 | 0 | — |
case-11 | pass→pass | 10,792 | 8,079 | -25% | 1 | 1 | 0% | 1,742 | 3,315 | +90% | 0 | 0 | — |
case-12 | fail→pass | 9,286 | 1,893 | -80% | 1 | 1 | 0% | 1,670 | 2,568 | +54% | 0 | 0 | — |
case-13 | fail→pass | 11,902 | 6,812 | -43% | 1 | 1 | 0% | 2,309 | 3,545 | +54% | 0 | 0 | — |
case-14 | pass→pass | 6,175 | 1,330 | -78% | 1 | 1 | 0% | 1,277 | 2,415 | +89% | 0 | 0 | — |
case-15 | pass→pass | 8,968 | 3,147 | -65% | 1 | 1 | 0% | 1,378 | 2,763 | +101% | 0 | 0 | — |
case-16 | fail→pass | 6,928 | 2,003 | -71% | 1 | 1 | 0% | 1,101 | 2,553 | +132% | 0 | 0 | — |
case-17 | fail→pass | 20,705 | 5,837 | -72% | 1 | 1 | 0% | 2,931 | 3,324 | +13% | 0 | 0 | — |
case-18 | pass→pass | 17,118 | 11,598 | -32% | 1 | 1 | 0% | 3,223 | 4,469 | +39% | 0 | 0 | — |
case-19 | fail→pass | 11,067 | 7,287 | -34% | 1 | 1 | 0% | 2,488 | 3,897 | +57% | 0 | 0 | — |
case-20 | pass→pass | 8,059 | 7,284 | -10% | 1 | 1 | 0% | 1,700 | 3,599 | +112% | 0 | 0 | — |
case-21 | fail→fail | 21,860 | 15,738 | -28% | 1 | 1 | 0% | 3,609 | 5,436 | +51% | 0 | 0 | — |
case-22 | pass→pass | 5,775 | 1,720 | -70% | 1 | 1 | 0% | 1,074 | 2,518 | +134% | 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 +59 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.