The ReconX API is a RESTful API that lets you submit claims, upload remittances, and query reconciliation results programmatically. It works similarly to Safaricom Daraja — authenticate with your key and secret, use the returned token for subsequent requests.
Production: http://localhost/api/v1 Sandbox: http://localhost/api/v1 (use sandbox API keys — prefix rxk_test_)
All requests must use HTTPS. HTTP requests are automatically redirected. The API is versioned — /api/v1 will remain stable. New versions will be announced with a 6-month migration period.
ReconX uses a two-step auth flow. First exchange your API key + secret for a short-lived Bearer token (1 hour TTL). Use that token for all subsequent requests.
Exchange your API credentials for an access token. The token expires in 3600 seconds (1 hour).
| Parameter | Type | Required | Description |
|---|---|---|---|
| api_key | string | required | Your API key (starts with rxk_live_ or rxk_test_) |
| api_secret | string | required | Your API secret (shown once at creation) |
const { data } = await fetch('http://localhost/api/v1/auth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
api_key: 'rxk_live_Xk9mABCD...',
api_secret: 'your_api_secret'
})
}).then(r => r.json());
const token = data.access_token; // use for 1 hour
{
"success": true,
"data": {
"access_token": "eyJhbGciOiJIUzI1NiJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"organization": { "name": "Nairobi General", "currency": "KES" }
}
}Submit a single claim. If a claim with the same claim_number already exists, it is updated.
| Field | Type | Description | |
|---|---|---|---|
| claim_number | string | required | Your unique claim reference |
| payer_name | string | required | Name of the insurer/payer (e.g. SHA) |
| billed_amount | number | required | Total amount billed, in your org currency |
| submission_date | date | required | ISO date: YYYY-MM-DD |
| patient_name | string | optional | Full name of the patient/member |
| patient_id | string | optional | Member/patient ID number |
| provider_name | string | optional | Facility or provider name |
| service_date | date | optional | Date of service (YYYY-MM-DD) |
| paid_amount | number | optional | Amount paid so far (if known) |
| status | string | optional | submitted | paid | partially_paid | unpaid | denied |
| metadata | object | optional | Any additional key-value data to store with the claim |
await fetch('/api/v1/claims', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
claim_number: 'CLM-2024-001',
payer_name: 'SHA',
billed_amount: 25000,
submission_date: '2024-01-15',
patient_name: 'Alice Wanjiru',
service_date: '2024-01-12'
})
});List claims with optional filters. Returns paginated results (20 per page by default, max 100).
| Query param | Type | Description | |
|---|---|---|---|
| status | string | optional | Filter by status (paid, unpaid, partially_paid, denied) |
| payer_id | string | optional | Filter by payer ID |
| date_from | date | optional | Submission date from (YYYY-MM-DD) |
| date_to | date | optional | Submission date to (YYYY-MM-DD) |
| is_duplicate | boolean | optional | Filter duplicate claims (true/false) |
| per_page | integer | optional | Results per page (default 20, max 100) |
| page | integer | optional | Page number |
Upload a remittance file (CSV, Excel, or ODS). The file is processed asynchronously — reconciliation begins immediately. Poll the batch status endpoint for progress.
// Using FormData (browser or Node.js)
const form = new FormData();
form.append('file', fileBlob, 'sha-remittance.xlsx');
form.append('payer_name', 'SHA');
form.append('payment_date', '2024-01-20');
const { data } = await fetch('/api/v1/remittances/upload', {
method: 'POST',
headers: { 'Authorization': `Bearer ${token}` },
body: form
}).then(r => r.json());
// data.batch_id — use to poll for status
console.log(data.batch_id, data.status); // → "processing"Get the full reconciliation summary including total claimed, total remitted, variance, and exception counts.
{
"success": true,
"data": {
"summary": {
"total": 312,
"matched": 238,
"partial": 56,
"unmatched": 18,
"total_claimed": 4850000,
"total_remitted": 4620000,
"total_variance": -230000,
"underpaid_count": 43,
"overpaid_count": 7,
"underpaid_amount": 248000,
"overpaid_amount": 18000,
"unresolved_exceptions": 23
},
"currency": "KES"
}
}Configure a webhook URL in Settings → Webhooks. ReconX will POST to your URL when events occur. Every delivery is signed with HMAC-SHA256.
| Event | Triggered when |
|---|---|
| claim.submitted | A new claim is created via import or API |
| claim.updated | A claim status or amount changes |
| remittance.processed | A remittance batch finishes processing |
| reconciliation.matched | A claim is matched to a remittance |
| exception.created | A variance exception is flagged |
| duplicate.detected | A duplicate claim or payment is found |
| report.ready | An exported report is ready to download |
Every webhook includes an X-ReconX-Signature header containing sha256={hmac}. Verify it using your webhook secret from Settings.
// Node.js verification example
const crypto = require('crypto');
function verifyWebhook(rawBody, signature, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
);
}
// In your Express route:
app.post('/webhooks/reconx', express.raw({ type: '*/*' }), (req, res) => {
const sig = req.headers['x-reconx-signature'];
if (!verifyWebhook(req.body, sig, process.env.RECONX_WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body);
console.log('Event:', event.event, event.data);
res.status(200).send('OK');
});| Plan | Requests/hour | Requests/day | Response header |
|---|---|---|---|
| Starter | 1,000 | 10,000 | X-RateLimit-Remaining |
| Professional | 10,000 | 100,000 | X-RateLimit-Remaining |
| Enterprise | Custom | Custom | X-RateLimit-Remaining |
When rate limited, the API returns HTTP 429 with a reset_at timestamp. Implement exponential backoff in your integration.
Our team can help with API integration, custom webhooks, or SDK development.
Contact Developer Support