Sources and Raw Ingestion
This page is optional for headless integrations.
RobotMoney exposes /v1/sources/* for tenant-scoped source records and the Raw API for the evidence those sources produce. Live connectors such as Plaid can hold encrypted credentials and run sync cycles. A source with status: historical is provenance metadata only: it has no live provider connection, stores no credentials, and cannot sync. This lets imported or curated Ledger data name its origin without claiming that RobotMoney is actively connected to that system.
List source records
GET /v1/sources
Read one source record
GET /v1/sources/{source_id}
Connect or disconnect a live source
POST /v1/sources, DELETE /v1/sources/{source_id}
Trigger a live source sync
POST /v1/sources/{source_id}/sync
Push an artifact (file, URL, or provider webhook payload) into Raw
POST /v1/raw/ingest, POST /v1/raw/webhooks/{provider}
Read or tombstone a Raw artifact
GET /v1/raw/{raw_id}, DELETE /v1/raw/{raw_id}
Trigger asynchronous document extraction
POST /v1/raw/{raw_id}/extract
Read extraction status
GET /v1/raw/{raw_id}/extraction
Read the deterministic parser output for an artifact
GET /v1/raw/{raw_id}/parsed
Promote parsed Raw into typed Ledger rows
POST /v1/ledger/normalize (see Ledger API)
The "Source Types" table further down is the shared connector and artifact origin taxonomy. A source record uses type; an ingested artifact uses the corresponding source_type.
Source lifecycle status
status
Meaning
active
A configured source eligible for live sync
paused
A configured source whose sync is intentionally paused
error
A configured source that needs attention after a connection or sync error
disconnected
A former connection that is no longer available
historical
Provenance for prior imports or curated data, with no live connection
Historical source freshness is not_applicable, and attempts to sync one fail with raw_source_unsupported.
Ingest a Raw Artifact
Two body shapes are supported on POST /v1/raw/ingest: a binary upload via multipart/form-data, or a URL fetch via JSON. Both are idempotent by SHA-256: a re-submitted artifact (per tenant) returns the existing raw_id with deduplicated: true.
Bring your own source
Use source_type: other to submit an artifact from a source without a native RobotMoney connector. This route requires a bearer principal with raw:write: a human JWT, a registered SIWX agent JWT that was granted that scope, or an eligible synthetic-demo brain_sk_test_ key. brain_sk_live_ keys and keys on customer-data tenants are read-only. Core re-checks the tenant's ready_demo, synthetic_brightline_v1, and demo markers on every Raw-key use. A custom artifact is stored as raw evidence and is projected only after a compatible parser is registered. It is not a direct arbitrary Ledger-event write API.
Binary upload:
URL fetch:
Response (201 on first ingest, 200 on dedup):
Limits: 50 MB per artifact. Errors: 400, 401, 403, 413, 415, 429.
plaid, stripe, finch, and merge_accounting are reserved on this route: those source types are provider-authenticated only and may be created solely through their authenticated provider integration, so a caller cannot mint high-trust evidence by labeling an upload. Asserting any of them here returns raw_source_reserved.
Structured CSV uploads
csv_upload supports deterministic, customer-asserted CSV records when the ingestion envelope declares object_type. The supported values are counterparties, payables_invoices, receivables_invoices, payroll_runs, and tax_obligations.
The object type is required because payable and receivable invoice files can have identical columns. RobotMoney never infers their direction from filenames or row values. payables_invoices always projects payable obligations, while receivables_invoices always projects receivable obligations. A counterparties file creates only counterparties: fields such as first_seen remain reference metadata and cannot become transaction or due dates. Projected invoice reads include both counterparty_id and the resolved counterparty_name when the declared counterparty CSV has been ingested.
Required headers are validated per object type. Ingestion itself returns 201. An unsupported or undeclared CSV schema fails asynchronously and is reported by GET /v1/raw/{raw_id}/extraction as status: "failed" with an error object. It is never sent to the LLM document extractor. Legacy AR-aging and payroll-register uploads remain supported by their existing deterministic parsers.
Source Types
The source_type you tag an ingested artifact with. Used for routing to the right parser.
source_type
Typical Origin
plaid
Plaid bank-account artifacts (statements, transactions)
stripe
Stripe API objects
netsuite
NetSuite SuiteTalk extracts
merge_accounting
Merge.dev accounting integrations (QuickBooks, Xero)
finch
Finch payroll and HR provider extracts
email_inbound
Inbound email (e.g. invoices forwarded to a mailbox)
csv_upload
Direct CSV file upload
pdf_upload
Direct PDF / document upload
alchemy_wallet
On-chain EVM extractor output (Alchemy indexer)
eth_address
Watched address chain events
agent_contributed
Pushed by an external agent with raw:write scope
wiki_annotation
Human corrections via the Wiki annotate path (internal)
other
Universal fallback: any source with no native connector
Provider Webhooks
Connected providers (Plaid, Stripe, etc.) push events at:
This route has security: []. The HMAC signature replaces bearer auth. RobotMoney verifies the signature, stores the payload as one or more Raw artifacts, and returns 202 Accepted with { accepted: true, trace_id: "...", artifacts: 1 }, where artifacts is the count persisted (0 on an idempotent replay). A signature mismatch returns 401 with raw_webhook_signature_invalid.
Read a Raw Artifact
The signed URL is short-lived (10-minute TTL) and returns the bytes with Content-Disposition: attachment. The artifact itself lives in the tenant's Azure Blob partition. 404 if unknown, 410 if tombstoned.
Tombstone a Raw Artifact
204 No Content. The artifact becomes inaccessible and is filtered from Wiki, but the underlying bytes are retained per regulatory retention policy. Re-tombstoning returns 410.
Read the Parsed Form
After ingestion, deterministic parsers extract structured fields. Their output is queryable:
Parsed rows are append-only; a re-run with a new parser_version produces a new row rather than mutating the old one.
Trigger and Inspect Document Extraction
Start asynchronous extraction for an existing Raw artifact:
The route returns 202 while the job is queued or running, and 200 for an existing terminal success. A failed terminal job is returned as a 502 with its stored extraction error. Set retry: true to explicitly requeue a terminal job.
Poll its current state with raw:read:
Promoting Raw to Ledger
Parsed Raw becomes typed Ledger rows via POST /v1/ledger/normalize (documented in the Ledger API). Normalization is idempotent. The same raw_parsed_id produces the same Ledger row ids on re-run.
What's Next
Last updated
