FlowParse
Feature September 2026 15 min read

Single-call JSON for no-code apps

One request, one response — a document goes in, typed JSON comes out of the same call. Built for app builders like Retool, Bubble and Airtable, where a multi-step server-side pipeline isn't something you can easily orchestrate.

FlowParse
flowparse.io

Why "single call" is the whole feature

Most document-extraction systems are built as a pipeline — upload, classify, route to a type-specific extractor, validate, then fetch a result once processing finishes. That design makes sense for a custom backend with a queue and a worker. It's a genuine obstacle for someone building inside an app builder like Retool, Bubble or Airtable, where the natural unit of work is a single request that either succeeds or fails, made from a button click or a workflow step, with no easy place to hold state between multiple asynchronous stages.

This feature is that constraint turned into a design decision: one request to POST /extractreturns everything — classification, fields, line items, confidence — in the same response, so an app builder's single-request-single-response model is never fighting the shape of the API underneath it.

It sounds like a small implementation detail, but it changes what's realistic to build without writing custom backend code. A single call fits inside a Retool query, a Bubble workflow step, or an Airtable script exactly the way those platforms already expect an external API to behave — no special-case handling required for a response that arrives in pieces.

FlowParse
flowparse.io

What a multi-step pipeline costs inside an app builder

A classify-then-extract pipeline forces an app builder to do three things it's not naturally good at: hold the classification result somewhere between two calls, branch its own workflow logic on that result to pick the right second call, and handle the case where the two calls disagree or one fails independently of the other. Each of those is possible to build in Retool, Bubble or Airtable, but each one is exactly the kind of custom logic an app builder is meant to let you avoid.

A polling-based asynchronous API adds a second cost on top: the app has to check a status endpoint repeatedly until a result is ready, which in most app builders means either a loop that blocks the UI or a scheduled automation running outside the user's session — real engineering work for what should be a document upload.

Neither cost is hypothetical — both show up quickly the moment a citizen developer tries to implement a multi-step pipeline inside a tool that was never designed to hold intermediate state between requests. The workaround usually involves a hidden table just to store in-flight status, a pattern that adds real complexity to what should have been a straightforward feature.

Everything in one response

Document type, every field with its value, line items when the document has a table, and a confidence score per field — all present in the single response to POST /extract. There is no second endpoint to call for line items, no separate confidence-check request, and no classification step you need to run first to know which extractor to call.

Practically, this means the document type — invoice, receipt, bank statement — arrives as a field in the same object as everything else, so a single conditional in your app can branch its own logic on that type without a separate lookup call to determine it first.

The shape of the response, end to end

POST /extract — full response in one call
curl -X POST https://flowparse.io/api/v1/extract \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "file": "JVBERi0xLjcK...", "filename": "receipt.jpg" }'
# → {
#   "type": "receipt",
#   "pages": 1,
#   "price": { "eur": 0.035, "perPageEur": 0.035 },
#   "data": {
#     "type": "receipt",
#     "data": {
#       "merchant_name": "Corner Cafe",
#       "date": "2026-09-02",
#       "total": 14.80,
#       "currency": "USD",
#       "line_items": [
#         { "description": "Latte", "amount": 5.20, "confidence": 0.99 },
#         { "description": "Bagel", "amount": 4.10, "confidence": 0.97 }
#       ]
#     },
#     "confidence": { "merchant_name": 0.98, "date": 0.99, "total": 0.99 }
#   }
# }

Every value your app needs — to write into a table row, populate a form, or trigger a downstream condition — is already present in this one object, at whatever nesting level your app builder's data-binding UI expects.

No polling, no webhook, no second call

Because the call is synchronous, there's nothing to poll and nothing to wait for after the response arrives. An app builder's loading spinner, tied directly to the request in flight, is the entire "processing" state your UI needs to represent — no separate job-status field, no background check running after the user has moved on.

This also means a user never has to leave the tool and come back later to see a result — the interaction starts and finishes in the same moment they uploaded the document, which is the experience an internal tool's users generally expect from anything else in the app.

FlowParse
flowparse.io

Why the response binds directly to a table or form

Field names are consistent and predictable across document types where the concept applies — total, currency, date — so a Retool table column, a Bubble data type field, or an Airtable column mapping can reference the same path in the response regardless of which document type triggered the call, reducing how much conditional logic your app needs around the binding itself.

Consistency here compounds as an internal tool grows — a binding set up once for invoices, using the same field names another document type also returns, often needs little or no adjustment when a second document type is added later.

Confidence scores in the same payload

A confidence score travels with every field in the same response, not as a separate lookup — so a conditional in your app ("show this row in red if any confidence is below 0.9") reads directly from the same object the field values came from, with no second request to check reliability after the fact.

FlowParse
flowparse.io

Why the API holds no state between calls

Each call to /extractis entirely independent — nothing about one call affects how the next one is processed, and there's no session or ordering requirement. This matters specifically for a no-code app builder, where multiple users might trigger the same action concurrently and there's no natural place to enforce call ordering even if the API required it.

Statelessness also means there's nothing to clean up or expire on the API's side — a call made once, ten times, or ten thousand times behaves identically each time, which removes an entire category of bug (a stale cached classification, a session that timed out mid-pipeline) that a stateful multi-step system would otherwise need to guard against.

One call versus a classify-then-extract pipeline

Classify-then-extract pipelineSingle-call JSON
Two or more requests, state held between themOne request, one response
App branches on a classification result to pick the next callNo branching needed — the response already includes the type
A separate call or poll to fetch line items or confidenceLine items and confidence in the same payload
Failure modes across two systems to handleOne failure mode: the single call succeeds or it doesn't

The right-hand column isn't a smaller version of the left-hand one — it's a different shape entirely. Removing a step doesn't just save time; it removes an entire class of bug that only exists because two systems had to agree with each other across a gap.

A worked example: from upload to populated record

A Bubble workflow triggers on a file upload: one API Connector call to /extract, and the workflow's next step immediately creates a new Thing with the returned fields — merchant, date, total — set directly from the response, with a conditional step that flags the record for review if any confidence score in the response is below 0.9. No intermediate state, no second call, no separate status check.

The entire feature, end to end — upload trigger, API call, record creation, review flag — is four steps in Bubble's workflow editor, none of which required custom code. That compactness is a direct consequence of the single-call design; a multi-step pipeline would have needed at least twice as many steps just to manage the handoff between stages.

What a failed or uncertain call looks like

A document that genuinely can't be read returns a clear error in the same response cycle — not a job that silently never completes. A document that's readable but uncertain on some fields still returns successfully, with those fields present and their low confidence score visible, so your app's error handling only needs to cover a true failure, not an ambiguous partial state.

What happens if your app makes many calls at once

Each call is independent, so multiple users triggering the same action at the same time simply results in multiple concurrent calls — nothing about the single-call design requires serializing requests from your app. For genuinely high-volume batch use beyond typical internal-tool traffic, the concurrency patterns covered in the internal tools API page apply the same way here.

Testing the shape before you build the UI around it

POST /validate is free on every plan and returns the same response shape without running real extraction, which is the fastest way to confirm field names and nesting before wiring up table columns or form bindings in your app builder — no need to spend on real documents just to see the JSON structure.

This is a genuinely useful habit to build even after your integration is live — running a new document type against /validate before adding it to your app confirms the field shape you expect actually matches what comes back, catching a mismatched binding before it reaches a real user rather than after.

What this feature does not do

It doesn't decide where the data goes

Writing the response into a table, a form, or a downstream workflow step is entirely your app's own logic.

It doesn't hold any state for you

If your app needs to remember a result across sessions, that storage is your app builder's database, not the API.

It doesn't replace judgment on an uncertain field

A low confidence score is a signal for your app to act on, not a decision the API makes on your behalf.

It doesn't handle authentication or authorization for your app

Your API key authenticates your app to FlowParse — who's allowed to trigger the call inside your own tool is entirely your app builder's own access control.

Who this is built for

App builders on Retool, Bubble or Airtable

Where a single request-response cycle is the natural unit of work, not a multi-stage backend pipeline.

Citizen developers without backend infrastructure

Who need document data without standing up a queue, a worker or a webhook receiver.

Teams prototyping quickly

Wanting a working feature today, bound directly from an API response to a UI component.

Anyone replacing a fragile classify-then-extract integration

Built against an older, multi-step OCR pipeline that's awkward to orchestrate in a no-code tool.

Across all four, what unites them is a preference for a request-response mental model over a pipeline mental model — not a specific industry or team size. A single call fits naturally wherever "a user did something, now show them a result" is the shape of the interaction, which describes most of what an internal tool actually does.

Security and data handling

Uploads are encrypted with TLS from end to end.

Processing runs on infrastructure with SOC 2-aligned controls.

Original documents are deleted shortly after processing.

Nothing uploaded is ever used to train AI models.

Full details are on the security page.

Get your API key

Make a single call against a real document and see the whole response shape at once — no second request required to understand what you'll be binding to your app.

A free plan account gives you full accuracy against a smaller monthly allowance, so the first call you make is against genuine extraction, not a scaled-down demo mode with different behavior.

Frequently asked questions

See the full response in one call

Get a free API key and make your first single call against a real document.

Keep reading