REST API v1

API Reference

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.

Get API Access → API Status

Base URL

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.


Authentication

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.

POST /api/v1/auth/token

Exchange your API credentials for an access token. The token expires in 3600 seconds (1 hour).

Request body
ParameterTypeRequiredDescription
api_keystringrequiredYour API key (starts with rxk_live_ or rxk_test_)
api_secretstringrequiredYour 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
Response
{
  "success": true,
  "data": {
    "access_token": "eyJhbGciOiJIUzI1NiJ9...",
    "token_type":   "Bearer",
    "expires_in":   3600,
    "organization": { "name": "Nairobi General", "currency": "KES" }
  }
}

POST /api/v1/claims

Submit a single claim. If a claim with the same claim_number already exists, it is updated.

FieldTypeDescription
claim_numberstring required Your unique claim reference
payer_namestring required Name of the insurer/payer (e.g. SHA)
billed_amountnumber required Total amount billed, in your org currency
submission_datedate required ISO date: YYYY-MM-DD
patient_namestring optional Full name of the patient/member
patient_idstring optional Member/patient ID number
provider_namestring optional Facility or provider name
service_datedate optional Date of service (YYYY-MM-DD)
paid_amountnumber optional Amount paid so far (if known)
statusstring optional submitted | paid | partially_paid | unpaid | denied
metadataobject 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'
  })
});

GET/api/v1/claims

List claims with optional filters. Returns paginated results (20 per page by default, max 100).

Query paramTypeDescription
statusstringoptionalFilter by status (paid, unpaid, partially_paid, denied)
payer_idstringoptionalFilter by payer ID
date_fromdateoptionalSubmission date from (YYYY-MM-DD)
date_todateoptionalSubmission date to (YYYY-MM-DD)
is_duplicatebooleanoptionalFilter duplicate claims (true/false)
per_pageintegeroptionalResults per page (default 20, max 100)
pageintegeroptionalPage number

POST/api/v1/remittances/upload

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/api/v1/reconciliation/summary

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"
  }
}

Webhooks

Configure a webhook URL in Settings → Webhooks. ReconX will POST to your URL when events occur. Every delivery is signed with HMAC-SHA256.

Available events
EventTriggered when
claim.submittedA new claim is created via import or API
claim.updatedA claim status or amount changes
remittance.processedA remittance batch finishes processing
reconciliation.matchedA claim is matched to a remittance
exception.createdA variance exception is flagged
duplicate.detectedA duplicate claim or payment is found
report.readyAn exported report is ready to download
Verify webhook signatures

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');
});

Rate Limits

PlanRequests/hourRequests/dayResponse header
Starter1,00010,000X-RateLimit-Remaining
Professional10,000100,000X-RateLimit-Remaining
EnterpriseCustomCustomX-RateLimit-Remaining

When rate limited, the API returns HTTP 429 with a reset_at timestamp. Implement exponential backoff in your integration.

Need help with your integration?

Our team can help with API integration, custom webhooks, or SDK development.

Contact Developer Support