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.
Scanned 8/31/2026
Install to Claude Code
npx -y skills add alpacahq/alpaca-skills --skill journals --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Journals?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/alpacahq-journals)More formats (shields.io, HTML) on the badges page.
---
name: alpaca-broker-journals
description: 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.
---
# Alpaca Broker API — Journals
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.
## Reference
- Guide: `https://docs.alpaca.markets/docs/funding-via-journals`
- API ref: `https://docs.alpaca.markets/reference/createjournal`
- Live schema: `alpaca-docs` MCP → `get-endpoint` title `"Broker API"` path `/v1/journals`
## 1. Endpoints
| 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) |
## 2. JNLC vs JNLS
`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.
## 3. Idempotency-Key header — USE IT
Pass an `Idempotency-Key` header (≤128 chars; a client-generated UUID is recommended) on journal creates.
- Same key + **identical** body → returns the original journal (no duplicate).
- Same key + **different** body → `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.
## 4. Batch vs reverse-batch (JNLC only, all-or-nothing)
**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.
## 5. Status lifecycle
`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:**
1. **`executed` ≠ final.** Don't treat `executed` as irreversible — Alpaca cashiering can reverse a journal that wasn't permitted. Reconcile against later events.
2. **`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`.)
## 6. SSE journal events
`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`.
## 7. Constraints & gotchas
- **Eligibility:** the cash-pooling/journals use case requires Alpaca review and possibly a local license — check with counsel.
- **JNLS account states:** `to_account` must be `ACTIVE`; `from_account` must be `ACTIVE` or `CLOSE`.
- **Sufficient funds:** JNLC create → `403` if the amount isn't available; reverse-batch `403` = insufficient balance/assets.
- **Daily limits push to `pending`** (manual ops approval).
- **`GET /v1/journals` returns `422` if the result set exceeds 100,000 records** — always filter with `after`/`before`/`limit`.
- **Delete is pending-only:** `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.
- **Travel Rule:** include transmitter info on money-moving journals (required on all incoming deposits regardless of amount).
- **Sandbox fixtures:** put fixtures in `description` (e.g. `/fixtures/status=rejected/fixtures/`) to simulate `rejected`/`pending` outcomes for testing.
## 8. The sweep-account funding pattern (why journals exist)
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`.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!