FULLA LEDGER API WALKTHROUGH Requirements: Node.js 24 or later; no npm packages. Download the .mjs file and run it with node. This example was checked against the application's route contracts and tested with mocked responses. It has not been run against a paid production API account. It does not prove extraction accuracy or compatibility with any external accounting importer. Use the fictional PDF at: https://fullaledgerbankstatements.com/samples/fulla-fictional-statement/statement.pdf Save it locally as statement.pdf. The matching sample exports were generated from authored approved fixture rows, not from an automatic API extraction run. The live model may produce different rows; inspect its actual result. AUTHENTICATION AND COST Set FULLA_API_KEY in your process environment using your shell or secret manager. Do not put the key in command arguments, patch files, source control or logs. The API key must belong to your Fulla workspace. Uploading starts processing and may consume its page allowance. This is a live API, not a sandbox endpoint. Default origin: https://fullaledgerbankstatements.com For local development only, explicitly set FULLA_API_ORIGIN to a loopback origin, e.g. http://127.0.0.1:5173. Other alternate origins are rejected. EXPLICIT STEPS 1. Upload the fictional PDF with a unique idempotency key: node fulla-api-walkthrough.mjs upload --file statement.pdf --idempotency-key sample-upload-001 This sends POST /api/v1/statements as multipart/form-data. The response has results[].id, status and status_url. Copy the returned UUID for the next step. 2. Check progress yourself; the script never polls or sleeps: node fulla-api-walkthrough.mjs status --id STATEMENT_UUID A returned status_url can instead be passed with --url. GET returns a statement object with status, job and currentRevision. If needs_password is returned, use an unencrypted fictional PDF for this example; this CLI does not implement the separate password endpoint. Do not assume HTTP 202 means extraction finished. Stop and inspect failureCode if the job fails. 3. Fetch the ready result and review it against the source PDF: node fulla-api-walkthrough.mjs result --id STATEMENT_UUID GET /api/v1/statements/:id/result?raw=true includes revision, metadata and transactions with transaction_id, source_page and raw_fields. Check every date, description, debit/credit sign, duplicate, missing row and balance. Preserve this result securely if needed; it may contain document content. 4. If a correction is needed, create a local patch.json using the ACTUAL revision and transaction_id from step 3. Example shape for a sign correction: { "base_revision": 1, "idempotency_key": "sample-review-001", "operations": [ {"type":"set_field","transaction_id":"ACTUAL_TRANSACTION_UUID","field":"amount","value":"-84.50"}, {"type":"set_field","transaction_id":"ACTUAL_TRANSACTION_UUID","field":"debit","value":"84.50"}, {"type":"set_field","transaction_id":"ACTUAL_TRANSACTION_UUID","field":"credit","value":null} ] } The displayed ID is explanatory, not a usable UUID. Do not send this patch blindly: change only a row that needs correction in the actual result. node fulla-api-walkthrough.mjs review --id STATEMENT_UUID --patch patch.json PATCH /api/v1/statements/:id/review validates operations on the server and returns review. Refetch the result and inspect the new revision. 5. Only after reviewing the final rows, explicitly approve that exact revision: node fulla-api-walkthrough.mjs approve --id STATEMENT_UUID --revision 2 Replace 2 with the actual reviewed revision. POST approval accepts only {"revision":N}; it does not accept an idempotency key. The script never selects or approves a revision for you, and never retries an approval. 6. Request one approved statement's export: node fulla-api-walkthrough.mjs export --id STATEMENT_UUID --format csv --idempotency-key sample-csv-001 POST /api/v1/statements/convert?format=csv sends ids and idempotencyKey. CSV, XLSX and OFX return export_id, status_url and download_url with HTTP 202. format=json instead prints approved structured data directly, with no download URL. This CLI exports one statement; bulk requests are outside its scope. OFX is not a native QBO, QFX or IIF export. 7. Check the export, then download using its returned URL: node fulla-api-walkthrough.mjs export-status --id EXPORT_UUID node fulla-api-walkthrough.mjs download --url /api/exports/EXPORT_UUID/download --output sample.csv Replace every UUID with the actual returned ID. The download command refuses existing files. Choose a new explicit output path; it ignores server-provided filenames. Copy returned status/download URLs only from the configured API. FAILURES AND RETRIES 401: missing, invalid or revoked key. 403: workspace access/entitlement issue. 409: result not ready, stale revision, idempotency mismatch or unapproved result. Fetch current status/result and inspect the specific code before acting. 410: expired resource; the old ID cannot be recovered by retrying it. 429: rate or allowance limit; inspect the error and numeric Retry-After header. Other failures print HTTP status and a safe server code. No automatic retries. A timeout can leave a write outcome unknown. Check status first. For upload, review and export, reuse the SAME idempotency key only for the exact same operation/payload. Never silently change base_revision and resend a correction. The script rejects cross-origin status/download URLs and refuses redirects before sending credentials to another destination. Every request has a 30-second timeout. Review JSON operations are fully validated by the server. LOCAL VERIFICATION node fulla-api-walkthrough.mjs --help In a repository checkout, the mocked tests are: bun run test -- tests/unit/api-walkthrough.test.ts Tests do not upload documents, call an extraction model or spend API allowance.