Configuration pages: /v1/console — the built-in ops console (channels, rates, waterfall, bindings, period operations). All endpoints (except this page, /v1/health, the two Xero browser routes /v1/abil-xero/authorize and /callback, and the supplier webhook /v1/abil-webhooks/:channelId/delivery, which takes the channel's webhook secret) require
Authorization: Bearer <IP4X token> + X-Tenant-ID, verified live against the
IP4X stack. Machine-readable manifest: /v1/api-index.json.
| handler | name | licence key | flavour | status |
lesaka | Lesaka (EasyPay) | abil:lesaka | transactional | ⏳ pending |
intouch | InTouch (eKYC metering) | abil:intouch | metered | ✅ implemented |
google_maps | Google Maps Platform | abil:google | metered | ✅ implemented |
riskscape | RiskScape | abil:riskscape | metered | ⏳ pending |
firstdmt_acs | FirstDMT (Axiros AXESS.ACS) | abil:firstdmt | metered | ⏳ pending |
gti_los | GeoTerraImage Line of Sight | abil:gti | metered | ✅ implemented |
interstellio | Interstellio NebularStack (RADIUS) | abil:interstellio | metered | ✅ implemented |
kamstrup_ready | Kamstrup READy (water metering) | abil:kamstrup | metered | ✅ implemented |
meteosource | Meteosource weather | abil:weather | metered | ✅ implemented |
imis | IMIS (T-GIS) municipal system of record | abil:imis | metered | ✅ implemented |
amber_panic | Amber Innovations PanicApp (armed response) | abil:panic | transactional | ✅ implemented |
smsportal | SMSPortal (LINK Mobility) WhatsApp & SMS | abil:smsportal | metered | ✅ implemented |
xero | Xero (accounting) | abil:xero | metered | ✅ implemented |
| GET | /v1/abil-channels | List channels (secrets masked; secrets_set booleans) |
| POST | /v1/abil-channels | Create channel (configuration encrypted at rest) |
| GET | /v1/abil-channels/:id | Get channel |
| PUT | /v1/abil-channels/:id | Update channel (omitted secrets preserved) |
| DELETE | /v1/abil-channels/:id | Soft-delete channel |
| GET | /v1/abil-channels/available-handlers | Provider catalog for this licence |
| GET | /v1/abil-channels/handlers/:handler/schema | Configuration schema |
| GET | /v1/abil-channels/handlers/:handler/actions | Actions a handler exposes |
| POST | /v1/abil-channels/:id/execute | Execute an action {action, params, disposition?} — records CDRs. Auth: IP4X user with abil:channels:execute, OR X-ABIL-Service-Key carrying channels:execute for this channel (disposition is always billable; customer_ref must be the key's consumer or beneath it). Idempotency-Key header replays the stored response; 429 cap_exceeded/float_exhausted; 504 outcome_unknown after a vendor timeout |
| POST | /v1/abil-webhooks/:channelId/delivery | SMSPortal WhatsApp status receipts: SMSPortal's native bare array [{MessageId, Status, Timestamp…}] or {events:[…]} / {statuses:[…]} (1–500) → ingest_delivery through the executor; billable CDRs only for receipts that correlate to this channel's own send, priced at the category that send recorded. Auth: X-ABIL-Webhook-Secret (a custom header on the SMSPortal webhook) OR Authorization: Basic base64(<any user>:<webhook_secret>) — never path, query or body. 200 {data:{received, delivered, failed, ignored, malformed, cdr_uncorrelated?}}; every refusal (no/wrong secret, unknown, deleted, inactive or non-smsportal channel) the same 401 + WWW-Authenticate: Basic; 400 validation for a malformed batch; 429 per source IP (ABIL_WEBHOOK_RATE_PER_MIN) |
| GET | /v1/abil-cdrs/metrics | Metric registry |
| POST | /v1/abil-cdrs | Record a CDR (idempotent via client_reference) |
| GET | /v1/abil-cdrs | Query CDRs (period, metric_key, product_sku, handler, disposition…) |
| GET | /v1/abil-cdrs/summary/:period | Per-metric aggregates for a period |
| GET | /v1/abil-rates | Cost rate cards (negative = commission earned) |
| POST | /v1/abil-rates | Create a cost rate (effective-dated; supersede, never edit) |
| POST | /v1/abil-rates/:id/close | End-date a rate |
| POST | /v1/abil-rates/rate-period/:period | Rating pass (?reRate=true for month-end whole-volume correction) |
| GET | /v1/abil-sku-map | (metric_key, service_code) → product SKU |
| POST | /v1/abil-sku-map | Add a mapping (effective-dated; supersede, never edit) |
| POST | /v1/abil-sku-map/:id/close | End-date a mapping |
| DELETE | /v1/abil-sku-map/:id | Soft-delete a mapping |
| POST | /v1/abil-sku-map/restamp/:period | Re-resolve SKUs on a period (409 once pushed) |
| GET | /v1/abil-sku-map/coverage/:period | Mapped vs still on the metric-key fallback |
| GET | /v1/abil-consumers | Consumer registry (a per-tenant tree) |
| POST | /v1/abil-consumers | Register a consumer (optional parent_id = tree edge) |
| PUT | /v1/abil-consumers/:id | Update a consumer {name?, notes?, is_active?} — is_active: true reactivates one a blocking cap switched off |
| POST | /v1/abil-consumers/:id/keys | Issue a service key {name?, scopes?: [ingest|usage|channels:execute|accounting], allowed_channel_ids? (required with channels:execute or accounting)} — secret shown once |
| DELETE | /v1/abil-consumers/keys/:keyId | Revoke a service key |
| GET | /v1/abil-caps | Usage / spend caps (?scope=channel|consumer|tenant&scope_id=) |
| GET | /v1/abil-caps/usage | Caps with the current window's used vs limit |
| POST | /v1/abil-caps | Create a cap {scope, scope_id?, metric_key?, kind: quantity|cost, window: day|period, limit_value, currency?, soft_pct?, action: block|warn} |
| DELETE | /v1/abil-caps/:id | Remove a cap |
| GET | /v1/abil-services | Service groups: one service, several providers |
| GET | /v1/abil-services/status | Members in cost order + the margin verdict per service (?code=) |
| POST | /v1/abil-services | Create a service {code, name, product_sku, margin_floor_pct?=10} |
| PUT | /v1/abil-services/:code | Update a service (name, product_sku, margin_floor_pct 0–95 as a margin on price, is_active, notes) |
| DELETE | /v1/abil-services/:code | Remove a service |
| POST | /v1/abil-services/:code/members | Add a provider {abil_channel_id, action, metric_key, service_code?} |
| DELETE | /v1/abil-services/members/:id | Remove a provider |
| POST | /v1/abil-services/:code/execute | Run the service: margin floor (409 margin_breach; 503 margin_unverified), then cheapest live provider first, failover on supplier failure; 429 on the caller's own cap; 502 when all fail. Same auth, Idempotency-Key and service-key rules as channel execute; no supplier cost in the response |
| POST | /v1/abil-ingest | Consumer usage ingest — auth by X-ABIL-Service-Key; customer_ref from the key, not the body |
| GET | /v1/abil-periods/:period | Period status (open | rated | sealed) |
| POST | /v1/abil-periods/:period/seal | Seal a period — the read API then serves it |
| POST | /v1/abil-periods/:period/reopen | Audited reopen (reason required) -> rated |
| GET | /v1/usage | PULL: sealed rated usage per consumer per SKU; auth by service key, scoped to the caller subtree |
| GET | /v1/abil-reports/supplier/:period | Supplier usage/cost by metric (+ flat-fee amortisation) — ?format=csv |
| GET | /v1/abil-reports/billing/:period | What the next usage push will claim, per customer per SKU — ?format=csv |
| GET | /v1/abil-reports/tenant/:period | Tenant cost by SKU / customer / metric — ?format=csv |
| GET | /v1/abil-ledger/parties | Party registry |
| POST | /v1/abil-ledger/parties | Create party |
| GET | /v1/abil-ledger/schemes | Split schemes with lines |
| POST | /v1/abil-ledger/schemes | Create effective-dated waterfall scheme |
| POST | /v1/abil-ledger/allocate/:period | Allocate rated CDRs to ledger entries + float draws |
| GET | /v1/abil-ledger/report/:period | Party totals for a period |
| GET | /v1/abil-ledger/floats | Float balances per channel |
| POST | /v1/abil-ledger/float-events | Record top-up/adjustment |
| GET | /v1/abil-xero/status | Every Xero channel: organisation, legal name, base currency, status, last sync, last error, call-budget usage |
| POST | /v1/abil-xero/connect | Start a connection {abil_channel_id} → {authorize_url} (one-time, 5 minutes; abil:xero:connect) |
| GET | /v1/abil-xero/authorize | PUBLIC browser leg (?ticket=): sets the HttpOnly nonce cookie and 302s to Xero |
| GET | /v1/abil-xero/callback | PUBLIC: Xero redirects here; 302 to XERO_RETURN_URL?xero=choose|error&xero_state=… (never tokens) |
| GET | /v1/abil-xero/pending/:stateId | Organisations this approval returned (name, legal name, VAT number, currency, demo flag, already bound elsewhere) |
| POST | /v1/abil-xero/confirm | Bind one organisation to the channel {state_id, xero_tenant_id}; the others from that approval are disconnected |
| POST | /v1/abil-xero/:channelId/disconnect | Stop posting: remove the connection at Xero and revoke the grant once nothing uses it (posted documents stay in Xero) |
| GET | /v1/abil-xero/:channelId/mappings | Tax label → TaxType, revenue key → account code, clearing account, sundry contact name |
| PUT | /v1/abil-xero/:channelId/mappings | Upsert mappings {entries:[{kind: tax|account|setting, source_key, xero_value}]}; empty xero_value removes (abil:xero:map) |
| GET | /v1/abil-xero/:channelId/options | Live ACTIVE revenue/payment accounts and sales tax rates from the organisation (spends Xero calls) |
| POST | /v1/abil-xero/:channelId/validate | Check every mapping against the live organisation → {ok, problems, warnings} |
| POST | /v1/abil-accounting/:channelId/documents | Queue up to 500 canonical documents {source, documents[]} → 202. Auth: X-ABIL-Service-Key with the accounting scope for this channel, OR an IP4X user with abil:xero:map |
| POST | /v1/abil-accounting/:channelId/preview | Render the Xero payloads these documents would become — nothing queued, nothing sent |
| GET | /v1/abil-accounting/:channelId/documents | Outbox and ID map (?state=queued|synced|exception|blocked&kind=contact|invoice|credit_note|payment|allocation|void) |
| POST | /v1/abil-accounting/documents/:id/retry | Re-queue an exception after fixing its cause |
| POST | /v1/abil-accounting/:channelId/sync | Post this channel's due documents now instead of on the worker's next tick |
| GET | /v1/abil-accounting/:channelId/tie-out/:period | Month-end tie-out (YYYY-MM): what ABIL posted vs what the organisation holds |
| GET | /v1/abil-billing/bindings | (product_sku, customer_ref) → IP4X account item |
| POST | /v1/abil-billing/bindings | Create binding |
| DELETE | /v1/abil-billing/bindings/:id | Remove binding |
| POST | /v1/abil-billing/push/:period | Push counts-only usage to IP4X partner ingest |
lesaka.vp.vend lesaka.vp.topup lesaka.ppu.vend lesaka.bps.payment intouch.verification intouch.workflow intouch.consent intouch.tfs_screening google_maps.geocoding google_maps.places_details google_maps.autocomplete_request google_maps.autocomplete_session_unconcluded google_maps.dynamic_maps google_maps.static_maps riskscape.autocomplete riskscape.address_details riskscape.reverse_geocode riskscape.dynamic_maps riskscape.elevation_3d firstdmt_acs.device_licence firstdmt_acs.api_call gti_los.query interstellio.subscriber_active interstellio.subscriber_suspended interstellio.virtual_server interstellio.domain interstellio.api_call kamstrup_ready.read kamstrup_ready.meter_reporting kamstrup_ready.api_call smsportal.whatsapp.message smsportal.sms.part smsportal.api_call imis.get_build_pre_app imis.capture_complaint imis.get_complaint_status imis.get_consumers_account_data amber_panic.raise_alert amber_panic.update_location meteosource.point xero.document