Developer Guide - CIRS
API integration and automation
Last updated About 2 months ago

π» Developer Guide: Invoice Reconciliation API
Best for: API integration and automation
Time: 10 minutes
Technical level: Requires API access

Quick Start
This system provides an API for automated invoice reconciliation against your contracted courier rates.
Authentication
ExampleAuthorisation: Bearer YOUR_API_KEYGet your API key from Settings > API Key

Core Workflow
1. Set up couriers/services/pricing (one-time)
β
2. Import invoice data (POST /api/invoices)
β
3. System auto-validates against expected rates
β
4. Retrieve discrepancies (GET /api/invoices/{id}/discrepancies)
API Endpoints
1. Create Courier
ExamplePOST /api/couriers
Content-Type: application/json
{
"name": "DPD",
"code": "dpd"
}Response:
Example{
"id": "courier_123",
"name": "DPD",
"code": "dpd"
}2. Create Service
ExamplePOST /api/courier-services
Content-Type: application/json
{
"courier_id": "courier_123",
"name": "DPD Next Day",
"code": "next_day"
}3. Set Pricing
ExamplePOST /api/courier-services/{service_id}/pricing
Content-Type: application/json
{
"zone": "GB_MAINLAND",
"weight_bands": [
{"min": 0, "max": 1, "price": 4.40},
{"min": 1, "max": 2, "price": 5.30},
{"min": 2, "max": 5, "price": 8.50}
],
"surcharges": [
{"type": "fuel", "amount": 0.50},
{"type": "saturday", "amount": 2.50}
]
}4. Import Invoice
ExamplePOST /api/invoices
Content-Type: multipart/form-data
courier_id: courier_123
invoice_number: INV-2024-001
file: @invoice.csvColumn Mapping (JSON):
Example{
"tracking_number": "Tracking Number",
"weight": "Weight (kg)",
"destination_postcode": "Postcode",
"service_type": "Service",
"base_cost": "Base Cost",
"surcharges": {
"fuel": "Fuel Surcharge",
"saturday": "Saturday Fee"
}
}Response:
Example{
"invoice_id": "inv_456",
"status": "processing",
"lines_imported": 150
}5. Get Invoice Status
ExampleGET /api/invoices/inv_456Response:
Example{
"id": "inv_456",
"status": "completed",
"total_lines": 150,
"matched": 145,
"discrepancies": 3,
"ignored": 2,
"processing_time_ms": 2340
}6. Get Discrepancies
ExampleGET /api/invoices/inv_456/discrepanciesResponse:
Example{
"discrepancies": [
{
"line_id": "line_789",
"tracking_number": "DPD123456",
"type": "overcharge",
"expected_amount": 14.50,
"invoiced_amount": 15.95,
"variance_amount": 1.45,
"variance_percent": 10.0,
"reason": "Base cost exceeded contract rate",
"details": {
"weight": 8.5,
"zone": "GB_MAINLAND",
"expected_base": 14.00,
"invoiced_base": 15.45,
"fuel_surcharge": 0.50
}
}
]
}7. Get Unmatched Lines
Lines not present in your billing system:
ExampleGET /api/invoices/unmatched?status=not_matchedResponse:
Example{
"unmatched_lines": [
{
"tracking_number": "DPD789012",
"invoice_id": "inv_456",
"amount": 12.40,
"shipment_date": "2024-11-15",
"billed_to_customer": false
}
],
"total": 15
}
Billing API Integration
Fetch Customer Data
Pull existing customer rates from Billing API:
ExamplePOST /api/customers/{customer_id}/fetch-billing-dataThis imports:
Customer zones
Contracted rates
Weight classes
Surcharges
Response:
Example{
"steps_completed": 9,
"couriers_imported": 3,
"services_imported": 12,
"zones_imported": 5,
"rates_imported": 156
}Match to Billing API
Check if invoice line matches a charge in Billing API:
ExampleGET /api/invoice-lines/{line_id}/billing-matchResponse:
Example{
"steps_completed": 9,
"couriers_imported": 3,
"services_imported": 12,
"zones_imported": 5,
"rates_imported": 156
}
Webhooks
Subscribe to real-time events:
Available Events
invoice.completed- Invoice processing finisheddiscrepancy.detected- Overcharge/undercharge foundline.unmatched- Charge not found in billing system
Subscribe
ExamplePOST /api/webhooks
Content-Type: application/json
{
"url": "https://your-app.com/webhooks/invoice-reconciliation",
"events": ["invoice.completed", "discrepancy.detected"]
}Webhook Payload
Example{
"event": "discrepancy.detected",
"timestamp": "2024-11-20T10:30:00Z",
"data": {
"invoice_id": "inv_456",
"line_id": "line_789",
"variance_amount": 1.45,
"variance_percent": 10.0
}
}
Automation Example
Node.js: Automated Daily Reconciliation
Exampleconst axios = require('axios');
const FormData = require('form-data');
const fs = require('fs');
async function reconcileInvoice(courierCode, invoiceFile) {
const API_KEY = process.env.RECONCILIATION_API_KEY;
const BASE_URL = 'https://api.yourplatform.com';
// 1. Upload invoice
const form = new FormData();
form.append('courier_id', courierCode);
form.append('invoice_number', `INV-${Date.now()}`);
form.append('file', fs.createReadStream(invoiceFile));
const uploadRes = await axios.post(
`${BASE_URL}/api/invoices`,
form,
{
headers: {
'Authorization': `Bearer ${API_KEY}`,
...form.getHeaders()
}
}
);
const invoiceId = uploadRes.data.invoice_id;
console.log(`Invoice uploaded: ${invoiceId}`);
// 2. Wait for processing
let status = 'processing';
while (status === 'processing') {
await new Promise(resolve => setTimeout(resolve, 2000));
const statusRes = await axios.get(
`${BASE_URL}/api/invoices/${invoiceId}`,
{ headers: { 'Authorization': `Bearer ${API_KEY}` } }
);
status = statusRes.data.status;
}
// 3. Get discrepancies
const discrepancyRes = await axios.get(
`${BASE_URL}/api/invoices/${invoiceId}/discrepancies`,
{ headers: { 'Authorization': `Bearer ${API_KEY}` } }
);
const { discrepancies } = discrepancyRes.data;
// 4. Alert if significant overcharges
const significantOvercharges = discrepancies.filter(
d => d.variance_percent > 5 && d.type === 'overcharge'
);
if (significantOvercharges.length > 0) {
console.log(`π¨ Found ${significantOvercharges.length} significant overcharges!`);
// Send alert email/Slack/etc.
}
return {
invoiceId,
totalDiscrepancies: discrepancies.length,
totalVariance: discrepancies.reduce((sum, d) => sum + d.variance_amount, 0)
};
}
// Run daily via cron
reconcileInvoice('courier_123', './invoices/dpd-latest.csv');
Rate Limits
100 requests per minute per API key
Invoice processing: Max 10,000 lines per file
Bulk operations: Max 50 invoices per batch

Error Handling
Common Errors
401 Unauthorized
Example{
"error": "invalid_api_key",
"message": "API key is invalid or expired"
}400 Bad Request - Invalid Column Mapping
Example{
"error": "missing_required_column",
"message": "Invoice missing required column: 'tracking_number'",
"available_columns": ["Tracking", "Weight", "Cost"]
}422 Unprocessable - Pricing Not Set
Example{
"error": "pricing_not_configured",
"message": "No pricing configured for zone: GB_HIGHLANDS",
"service_id": "service_123"
}
Best Practices
Set up pricing before importing invoices - System can't validate without expected rates
Use postcode over zone for destination - More accurate matching
Handle ignored lines - Invalid data won't be processed
Monitor unmatched rates - Indicates missing customer billing
Store invoice_id - Use for future reference and reporting

Testing
Sandbox Environment:
Base URL: https://sandbox-api.yourplatform.com
Test API Key: Available in Settings (sandbox mode)Test Invoice: Use /api/test/generate-sample-invoice to get test data

Support & Next Steps π
Need Help?
Contact Support.
Developers: See API Documentation.
Billing Questions: Contact your account manager.
Related Guides
