Building an AP Automation Pipeline with an OCR API and Webhooks
Learn how to build a reliable accounts payable automation pipeline. Use an async OCR API to upload invoices and receive structured data via secure webhooks.
Processing invoices is a classic bottleneck. Manual data entry is slow and error-prone. Even with an OCR API, building a system that polls for job completion is inefficient. It adds complexity, latency, and unnecessary traffic to your system. A truly automated accounts payable workflow shouldn't involve waiting and asking "are we there yet?" It should be event-driven, responding instantly when data is ready.

This is the core principle behind space-ocr's asynchronous processing. Instead of making a request and holding the connection open, you can upload a batch of invoices and immediately get back to work. The API returns a list of job identifiers, confirming the files were received. Our engine then processes each image. When an invoice is successfully read, a notification is sent directly to your application's endpoint. This is done using a webhook—a simple, reliable HTTP POST request containing the structured data.
The flow is straightforward: you make one POST /upload call with your invoice images. In response, you get an array of pending jobs. Later, your server receives an ocr.completed event via webhook. The payload of that event contains the full extraction result, from the supplier name, like "弥生サンブル", down to each line item with its associated coordinates on the page. For reliability, every upload can include an Idempotency-Key header. If you need to retry a network request, sending the same key guarantees you won't create a duplicate processing job.
const express = require('express');
const crypto = require('crypto');
// Middleware to get the raw body for signature verification
const rawBodySaver = (req, res, buf, encoding) => {
if (buf && buf.length) {
req.rawBody = buf.toString(encoding || 'utf8');
}
};
const app = express({ verify: rawBodySaver });
const webhookSecret = process.env.SPACE_OCR_WEBHOOK_SECRET;
app.post('/webhook', (req, res) => {
const signature = req.get('X-Spaceocr-Signature');
if (!signature) {
return res.status(400).send('Missing signature');
}
const hmac = crypto.createHmac('sha256', webhookSecret);
const digest = Buffer.from(hmac.update(req.rawBody).digest('hex'), 'hex');
const receivedSignature = Buffer.from(signature, 'hex');
if (!crypto.timingSafeEqual(digest, receivedSignature)) {
return res.status(400).send('Invalid signature');
}
// Signature is valid, now process the event
const event = JSON.parse(req.rawBody);
switch (event.event) {
case 'ocr.completed':
const { result } = event.data;
console.log(`OCR successful for ${result.name}:`, result.values);
// TODO: Add structured data to your accounting system
break;
case 'ocr.failed':
const { error } = event.data;
console.error(`OCR failed for ${event.data.name}:`, error.message);
break;
case 'webhook.test':
console.log('Webhook test successful!');
break;
default:
console.log(`Unhandled event type: ${event.event}`);
}
res.status(200).send({ received: true });
});
app.listen(3000, () => console.log('Webhook receiver listening on port 3000'));How does space-ocr locate data on the page? The underlying language model returns extracted text along with token hints pointing to their likely location. Our engine then performs a crucial verification step: it matches the extracted value, character-by-character, against the actual OCR symbols detected on the page. This process generates a match_ratio score from 0.0 to 1.0. A score of 0.85 or higher indicates a confident match. The final coordinates are returned as a 0-1000 normalized bounding box, independent of the original image's pixel dimensions.
Building this kind of automation should be accessible. The pricing is a flat $0.05 ($0.05 USD) per image processed through the API. Every account gets 100 free scans each month. Importantly, if an OCR job fails because an image is unreadable, you are not charged. The cost is tied directly to successful data extraction, making it a low-risk way to start automating your payables.
- Expose a Public EndpointYour server needs a public URL. Use a service like ngrok for local development to expose your local server to the internet.
- Register Your Webhook URLIn the space-ocr dashboard settings, add your public endpoint URL. Securely copy the generated webhook secret.
- Implement Signature VerificationUse the provided code sample to verify the `X-Spaceocr-Signature` header on every incoming request to ensure it's authentic.
- Handle the 'ocr.completed' EventWhen a valid event arrives, parse the `data.result` object and pass the structured invoice data to your accounting system or database.
- Upload an Invoice AsynchronouslyMake a `POST /upload` request with your files. The API will immediately return job details without waiting for OCR to finish.
- Acknowledge Events and MonitorYour endpoint should return a 2xx status code quickly to acknowledge receipt. Monitor delivery logs in the dashboard for any issues.
What happens if my webhook endpoint is down?
How do I verify that a webhook request is actually from space-ocr?
Can I upload multiple invoices in one API call?
Is there a charge for failed OCR jobs?
What's the difference between polling and using webhooks?
How can I safely retry uploads from my application?
Start Automating Your AP Workflow
Get your API key and start building with 100 free scans every month.