IDaaS — Integration Guide
Audience: Engineers integrating a client application with the IDaaS platform.
Base URL:https://<your-idaas-host>/api(all paths below omit this prefix)
Interactive docs:GET /swagger-ui.html· Machine-readable spec:GET /v3/api-docs
Table of Contents
- What IDaaS Is
- Core Concepts
- How the Platform Works End to End
- Quick-Start Checklist
- Step 1 — Register Your Application ← includes
appHandle - Step 2 — Authenticate (Get a Token)
- Step 3 — Create Subjects and Tags
- Step 4 — Initiate a Payment
- Step 5 — Receive and Handle Webhooks
- Step 6 — Accept or Reject a Transaction
- Step 7 — Monitor Webhooks and Delivery
- Step 8 — Wallet and Ledger
- Step 9 — Settlement
- Payload Encryption (JWE)
- Webhook Signature Verification
- Idempotency
- Rate Limiting
- Cross-App Tag Claims and Consent
- Namespaced Tags — Onboarding Existing Tag Systems ← new
- Tag Federation
- Error Handling
- API Reference Summary
- Integration Flows — Sequence Diagrams
1. What IDaaS Is
IDaaS (Identity as a Service) is a multi-tenant platform that provides:| Capability | What it means for your app |
|---|---|
| Identity tags | Every user gets a portable handle (e.g. alice) scoped to your app’s namespace, forming a globally unique qualified address (alice@walletapp) that works across all apps on the platform |
| Cross-app payments | Your app can send money to a tag on any other app, and receive money from any tag, without needing a direct integration with the other app |
| Consent-based claims | If two apps both know the same user, either can request a verified link (claim) between the user’s tags via owner-approved consent |
| Application wallets | IDaaS maintains a single escrow wallet per application that tracks its net monetary position from all completed transactions |
| Double-entry ledger | Every accepted transaction creates immutable DEBIT + CREDIT ledger entries with opening/closing balances |
| Settlement | A scheduled end-of-day job (23:59 UTC) produces per-application net position reports for financial reconciliation |
| JWE encryption | Every request body and webhook payload can be encrypted end-to-end using EC P-256 keypairs |
2. Core Concepts
Application
The top-level entity in IDaaS — it represents your service. Each application gets:- A
clientIdandclientSecretfor authentication - A
webhookSecretfor verifying inbound webhook signatures - An EC P-256 keypair for payload encryption
- One escrow wallet for net monetary position tracking
Subject
A user identity within your application. A subject maps your internal user ID (externalId) to the IDaaS system. One subject can own many tags.
Tag
A portable identity handle (e.g.alice, shop-main) scoped to the creating application. Each application has a short, unique appHandle slug (e.g. walletapp). The tag’s globally unique identifier is the qualified address: localTag@appHandle (e.g. alice@walletapp).
This means two different applications can each have a user named alice without any conflict — they become alice@walletapp and alice@shopapp, clearly distinct identities.
Tag rules:
localTagpart: 3–64 characters, lowercase alphanumeric + hyphens, not starting or ending with a hyphenappHandlepart: 3–30 characters, lowercase alphanumeric + hyphens, not starting or ending with a hyphen; set at registration time and immutable- Tags can be resolved publicly using either the bare local name (
alice) or the qualified address (alice@walletapp) - A bare name lookup that matches tags in more than one application returns 409 Conflict — use the qualified address in that case
- Can receive payments from any other tag on any application
Transaction
A cross-application payment from one tag to another. Transactions go through a two-step lifecycle — the sending application initiates, the receiving application explicitly accepts or rejects.Wallet
IDaaS maintains oneApplicationWallet per application. It tracks the cumulative credits and debits from all completed transactions. This is an escrow/settlement wallet, not an end-user wallet — per-user balances remain your application’s responsibility.
Webhook
An outbound HTTP POST from IDaaS to your application notifying you of transaction events. There are two distinct notifications per transaction:- RECEIVER webhook — sent to the receiving app’s
transactionWebhookUrlwhen a transaction is addressed to one of its tags - SENDER callback — sent to the
callbackUrlprovided by the sending app when the transaction outcome is determined
3. How the Platform Works End to End
4. Quick-Start Checklist
- Register your application (
POST /v1/applications) — saveclientSecret,webhookSecret, and key material; they are shown only once - Authenticate (
POST /v1/auth/token) — obtain a Bearer JWT - Provision wallet (
POST /v1/wallet) — idempotent, safe to call on every startup - Create subjects for your users (
POST /v1/subjects) - Create tags for your users (
POST /v1/tags) - Expose a webhook endpoint at your
transactionWebhookUrlto receiveTRANSACTION_INITIATEDevents - Verify webhook signatures using
X-IDaaS-Signatureon every inbound webhook - Respond with 2xx within 10 seconds on webhook delivery; IDaaS retries 3 times
- Set up a callback endpoint for
TRANSACTION_COMPLETED/TRANSACTION_REJECTED/TRANSACTION_EXPIREDnotifications
5. Step 1 — Register Your Application
Registration is public (no auth required). Each application registration creates an independent tenant.Request
| Field | Required | Description |
|---|---|---|
name | ✅ | Human-readable application name (2–150 chars) |
appHandle | ✅ | Immutable namespace slug for your tags (3–30 chars, lowercase alphanumeric + hyphens, no leading/trailing hyphen) |
transactionWebhookUrl | Recommended | Your HTTPS endpoint that IDaaS POSTs TRANSACTION_INITIATED events to |
redirectUris | Optional | Space-separated OAuth2 redirect URIs |
metadata | Optional | Free-form JSON string for your own use |
Response
⚠️ Store immediately and securely:Lose any of these and you must rotate (use key/webhook secret rotation endpoints, or deactivate + re-register).
clientSecret— used to obtain Bearer tokens; never shown againwebhookSecret— used to verifyX-IDaaS-Signatureon every inbound webhook; never shown againappPrivateJwk— your application’s private EC key (containsd); required to decrypt JWE-encrypted webhook payloads; never shown againappHandle— returned in the response and inGET /v1/applications/{id}; it is the namespace for all your tags (e.g.alice@walletapp). Immutable.
6. Step 2 — Authenticate (Get a Token)
All protected endpoints require a Bearer JWT. Tokens expire after 1 hour (configurable viaIDAAS_APP_TOKEN_TTL).
Request (form-encoded)
Request (JSON body)
Response
Using the token
Add the JWT to every subsequent request:Rate limit: 10 requests / minute per IP. Implement token caching — refresh proactively before expiry rather than waiting for a 401.
Token refresh pattern
7. Step 3 — Create Subjects and Tags
7.1 Create a Subject
A subject represents one of your users inside IDaaS. Create a subject for every user who needs a tag.| Field | Required | Description |
|---|---|---|
externalId | ✅ | Your internal user ID (max 255 chars) — must be unique within your app |
displayName | Optional | Shown on consent pages (max 150 chars) |
email | Optional | Used for consent notification delivery (max 200 chars) |
metadata | Optional | Free-form JSON string |
id (IDaaS subject UUID) — you need it to create a tag.
7.2 Create a Tag
| Field | Required | Validation |
|---|---|---|
tag | ✅ | 3–64 chars; lowercase alphanumeric + hyphens; must not start or end with a hyphen |
subjectId | ✅ | IDaaS subject UUID — must belong to your application |
appHandle: alice → alice@walletapp.
ThequalifiedAddress(alice@walletapp) is the canonical cross-platform identifier for this tag. Share it with other applications so they can address payments to it unambiguously.
7.3 Resolve Any Tag (Public, Cached)
Any application can look up any tag — no auth required. Results are cached for 30 seconds. You can resolve by bare local name or by qualified address:
If alice exists on more than one application, the bare-string lookup returns 409 Conflict with the list of qualified addresses. Use the qualified address to avoid ambiguity.
ACTIVE.
8. Step 4 — Initiate a Payment
The sending application calls this endpoint to begin a cross-application payment.Tag addressing: BothsenderTagandreceiverTagaccept either a bare local name (alice) or a qualified address (alice@walletapp).
Use the qualified address whenever possible — bare names that exist on more than one application return a 409 Conflict asking you to specify the qualified address.
| Field | Required | Description |
|---|---|---|
senderTag | ✅ | Bare name or qualified address. Must belong to the calling application (403 otherwise). Prefer qualified address (alice@walletapp). |
receiverTag | ✅ | Bare name or qualified address on any application. Prefer qualified address (bob@shopapp). |
amount | ✅ | Positive, up to 15 integer + 4 decimal digits (e.g. 1500.0000) |
currency | Optional | 3-letter ISO 4217 code; defaults to NGN |
narration | Optional | Payment note shown to both parties (max 500 chars) |
callbackUrl | Optional | Your HTTPS URL for the outcome callback — overrides your registered transactionWebhookUrl for this specific transaction’s sender callback. If omitted, IDaaS falls back to the sending application’s registered transactionWebhookUrl. |
Idempotency-Key header | Recommended | Unique string (UUID v4 recommended, max 64 chars); duplicate calls within 24 hours return the original response |
Response
Transaction responses always return qualified addresses insenderTagandreceiverTag(e.g.alice@walletapp,bob@shopapp).
What happens next
- IDaaS persists the transaction in
AWAITING_ACCEPTANCEstate - IDaaS persists a
TransactionWebhookrecord (direction=RECEIVER, status=PENDING) - IDaaS publishes a
WebhookDispatchEventto theidaas.webhooksKafka topic - The
WebhookDispatchConsumerPOSTs aTRANSACTION_INITIATEDnotification to the receiver app’stransactionWebhookUrl(asynchronously) - The transaction auto-expires after 24 hours if not accepted
9. Step 5 — Receive and Handle Webhooks
IDaaS POSTs notifications to yourtransactionWebhookUrl. You must:
- Expose a publicly reachable HTTPS endpoint
- Verify the
X-IDaaS-Signatureheader (see §15) - Return
2xxwithin 10 seconds; IDaaS retries up to 3 times (after 1 s and 2 s back-off)
9.1 RECEIVER Webhook — TRANSACTION_INITIATED
Sent to the receiving application when a new payment is addressed to one of its tags.
- Verify the signature (see §15)
- Parse the
reference— this is the key you’ll use to accept or reject - Apply your own business logic (check funds, validate the order, etc.)
- Call
POST /v1/transactions/{reference}/acceptor.../rejectwithin 24 hours - Return
200 OKimmediately — do your business logic asynchronously if needed
9.2 SENDER Callback — Outcome Notification
Sent to the sending application’s transaction-specificcallbackUrl when provided; otherwise IDaaS falls back to the sending application’s registered transactionWebhookUrl after the transaction outcome is determined.
event value | Meaning |
|---|---|
TRANSACTION_COMPLETED | Receiver accepted; wallets have been debited/credited |
TRANSACTION_REJECTED | Receiver rejected; no money moved |
TRANSACTION_EXPIRED | 24-hour window elapsed without acceptance; no money moved |
TRANSACTION_UPDATED | Intermediate state change (rare) |
9.3 Webhook Endpoint Implementation Checklist
10. Step 6 — Accept or Reject a Transaction
Only the receiving application can accept or reject. IDaaS enforces this — calling accept/reject with the wrong token returns403.
Accept
- Sender’s wallet is debited by
amount - Receiver’s wallet is credited by
amount - Two immutable
LedgerEntryrows are written - Transaction status moves to
COMPLETED - Sender’s
callbackUrlreceivesTRANSACTION_COMPLETED(or the sender app’stransactionWebhookUrlif no transaction-specific callback was supplied)
Reject
reason field is optional (max 500 chars). On success:
- No ledger movement
- Transaction status moves to
REJECTED - Sender’s
callbackUrlreceivesTRANSACTION_REJECTED(or the sender app’stransactionWebhookUrlif no transaction-specific callback was supplied)
Error responses for accept/reject
| Status | Meaning |
|---|---|
403 | Your application is not the receiver of this transaction |
404 | Transaction reference not found |
409 | Transaction is not in AWAITING_ACCEPTANCE state (already completed, rejected, or expired) |
11. Step 7 — Monitor Webhooks and Delivery
Check delivery status for a transaction
RECEIVER and one SENDER:
Webhook delivery statuses
| Status | Meaning |
|---|---|
PENDING | Queued, not yet attempted or in progress |
DELIVERED | HTTP 2xx received from your endpoint |
FAILED | All 3 attempts exhausted; event dead-lettered to idaas.webhooks.dlt |
Find all failed webhooks for your app
12. Step 8 — Wallet and Ledger
Provision wallet (call on startup)
Get current balance
balance = totalCredited − totalDebited. A positive balance means you have net received more than you sent. A negative balance means you have net sent more than you received.
Get ledger statement (paginated)
entryType | Meaning |
|---|---|
CREDIT | Money came in (you were the receiver) |
DEBIT | Money went out (you were the sender) |
13. Step 9 — Settlement
IDaaS automatically runs an end-of-day settlement job at 23:59 UTC every day. It aggregates all completed transactions for the day and produces per-application net positions.Get today’s settlement (after 23:59 UTC)
Manual trigger (operational use)
14. Payload Encryption (JWE)
WhenencryptionEnabled=true for your application, all request bodies must be JWE-encrypted and all webhook payloads you receive will be JWE-encrypted. This provides end-to-end payload confidentiality on top of TLS.
Algorithm
| Parameter | Value |
|---|---|
| Key agreement | ECDH-ES+A256KW |
| Content encryption | A256GCM |
| Key type | EC P-256 (secp256r1) |
14.1 Encrypting Requests Sent TO IDaaS
14.2 Decrypting Webhooks Received FROM IDaaS
WhenContent-Encryption: JWE is present on an inbound webhook, the body is a JWE compact string encrypted with your application’s EC public key. Decrypt it with your private key (appPrivateJwk) received once at registration/rotation.
14.3 Key Rotation
Rotate your keypair periodically or after a key compromise:appPublicKeyJwk and appPrivateJwk — store the private JWK immediately, it is shown only once.
After rotation:
- IDaaS encrypts all subsequent webhooks with the new public key
- Requests encrypted with the old public key will be rejected
- Allow a brief overlap window for in-flight messages before destroying the old private key
14.4 Webhook Secret Rotation
If your application loses its webhook secret (or suspects compromise), rotate it without re-registering:webhookSecret — store it immediately, it is shown only once.
After rotation:
- IDaaS signs all subsequent webhooks with the new secret
- Verification with the old secret fails immediately
15. Webhook Signature Verification
Every outbound webhook POST from IDaaS includes two headers:Signature algorithm
webhookSecret is the raw (unhashed) secret returned at registration.
It can also be rotated via POST /v1/applications/webhook-secret/rotate if lost or compromised.
Verification (pseudo-code)
Implementation examples
Java:16. Idempotency
POST /v1/transactions supports the Idempotency-Key request header. Supplying the same key within 24 hours returns the original response without creating a duplicate transaction.
- Use UUID v4 as idempotency keys
- Generate the key before the first attempt and store it alongside your order/payment record
- Re-use the same key on all retries of the same logical payment
- Do not re-use keys across different payments
17. Rate Limiting
| Endpoint | Limit | Key |
|---|---|---|
POST /v1/auth/token | 10 requests / min | Per IP |
POST /v1/transactions | 60 requests / min | Per application |
| All other endpoints | 300 requests / min | Per IP |
Handling 429 in your integration:Retry-After— number of seconds until the bucket refills. Read this value and wait at least that long before retrying.
X-RateLimit-Remaining— tokens remaining in the current window;0when you are throttled.
18. Cross-App Tag Claims and Consent
If your application needs to associate one of your users with a tag they own on another application (e.g.bob on ShopApp), you can request a claim through the consent flow.
18.1 Request a Claim
18.2 Consent Approval Flow
18.3 After Approval
On approval, IDaaS returns an attestation JWT:19. Namespaced Tags — Onboarding Existing Tag Systems
The problem
Many applications that integrate IDaaS already run their own tag / username system. When they onboard their users onto IDaaS, tag collisions are inevitable: WalletApp may already have a useralice, and so does ShopApp — completely different people.
A single global UNIQUE(tag_string) constraint would block one application from onboarding. IDaaS solves this with qualified addressing.
How it works
Every application registers with a short, uniqueappHandle slug (e.g. walletapp). Every tag automatically derives a qualified address localTag@appHandle:
| App | Local tag | Qualified address |
|---|---|---|
WalletApp (appHandle = walletapp) | alice | alice@walletapp |
ShopApp (appHandle = shopapp) | alice | alice@shopapp |
What stays the same for the host app
Your application continues to use its own tag system internally. IDaaS only requires that you onboard the tags you want to be discoverable cross-platform:- Register on IDaaS with your
appHandle - Create a Subject for each user:
POST /v1/subjects - Create a Tag for each user you want discoverable:
POST /v1/tagswith{"tag": "alice", "subjectId": "..."} - IDaaS records
alice@walletappas the globally unique qualified address - Other applications discover your users via
GET /v1/tags/alice@walletapp - Payments are routed to
alice@walletapp— your internal tag system is unaffected
Address resolution behaviour
| What you send | Behaviour |
|---|---|
alice@walletapp | Looks up by qualified address — fast, unambiguous, always recommended |
alice | Looks up by bare local name — succeeds only if exactly one application has a tag named alice; returns 409 Conflict with the list of available qualified addresses if more than one app does |
Example: sending a payment to a user on another app
alice is unique on the platform and bob is unique on the platform. If either is ambiguous, IDaaS returns 409 and lists the qualified addresses to use.
appHandle rules
| Rule | Detail |
|---|---|
| Length | 3–30 characters |
| Characters | Lowercase letters, digits, hyphens — no spaces or special characters |
| Position | Must not start or end with a hyphen |
| Uniqueness | Must be globally unique across all applications |
| Immutability | Cannot be changed after registration. Changing it would break all published qualified addresses. |
20. Tag Federation
Federation links a tag to an identity on an external identity provider (e.g. “alice on WalletApp is the same person as alice@github.com”). This is for advanced cross-platform identity scenarios.21. Error Handling
All IDaaS responses use a consistentApiResponse<T> envelope:
errorCode is the stable machine-readable identifier (for example IDAAS-AUTH-1002).
Client applications should branch handling logic by errorCode, not by free-text message.
Complete error code catalog
All IDaaS errors are returned with a stable error code in theerrorCode field. Client applications should parse and branch on this code rather than free-text messages, enabling reliable structured error handling.
Authentication & Authorization (1000–1999)
| Code | HTTP | Message | Cause | Resolution |
|---|---|---|---|---|
IDAAS-AUTH-1001 | 401 | Authentication required | Missing/invalid Authorization header | Provide Bearer token: Authorization: Bearer <token> |
IDAAS-AUTH-1002 | 401 | Invalid JWT | Token is malformed, expired, or has invalid signature | Re-authenticate via POST /v1/auth/token |
IDAAS-AUTH-1003 | 401 | Token has been revoked | Application was deactivated after token issuance | Re-authenticate with active application credentials |
IDAAS-AUTH-1004 | 401 | Authentication failed | Client ID/secret mismatch, malformed Basic auth | Verify credentials and application is active |
IDAAS-AUTH-1005 | 401 | Application not found | Application in JWT subject does not exist | Verify application ID and registration status |
IDAAS-AUTH-1006 | 403 | Access denied | Authenticated but unauthorized for this operation | Use correct role/credentials for operation |
IDAAS-AUTH-1007 | 403 | Transaction is not addressed to the calling application | Receiver attempting to act on sender’s transaction | Use credentials of the receiving application |
IDAAS-AUTH-1008 | 403 | Identity link is not associated with the calling application | Application does not own this resource | Use credentials of the owning application |
Validation Errors (2000–2999)
| Code | HTTP | Message | Cause | Resolution |
|---|---|---|---|---|
IDAAS-VALID-2001 | 400 | Validation failed | Field-level validation errors (required fields, format, length, etc.) | Fix request payload per errors list and retry |
IDAAS-VALID-2002 | 400 | keyword is required | Search endpoint called without keyword query param | Provide non-empty keyword parameter |
IDAAS-VALID-2003 | 400 | keyword must be at least 2 characters | Search keyword is too short | Use keyword with ≥ 2 characters |
IDAAS-VALID-2004 | 415 | Unsupported media type | Content-Type not application/json, or JWE payload misformatted | Send JSON with Content-Type: application/json; for JWE set Content-Encryption: JWE |
IDAAS-VALID-2005 | 400 | Decryption failed | JWE payload could not be decrypted or is malformed | Verify encryption key and payload format |
Resource Not Found (3000–3999)
| Code | HTTP | Message | Cause | Resolution |
|---|---|---|---|---|
IDAAS-RES-3001 | 404 | Application not found | Application UUID does not exist | Verify application UUID; register if needed |
IDAAS-RES-3002 | 404 | Subject not found | Subject UUID does not exist in calling application | Verify subject ID; create via POST /v1/subjects if needed |
IDAAS-RES-3003 | 404 | Subject not found with external ID | Subject with given external ID does not exist | Verify external ID; create subject if needed |
IDAAS-RES-3004 | 404 | Tag not found | Tag (qualified address or bare name) cannot be resolved | Verify tag exists; use qualified address tag@appHandle if bare name is ambiguous |
IDAAS-RES-3005 | 404 | Claim not found | Claim UUID does not exist | Verify claim ID |
IDAAS-RES-3006 | 404 | Claim not found with consent token | Consent token is invalid or expired | Request a new claim to get fresh consent token |
IDAAS-RES-3007 | 404 | IdentityLink not found | Identity link UUID does not exist or was revoked | Verify identity link ID |
IDAAS-RES-3008 | 404 | IdentityLink not found with challenge token | Challenge token is invalid, expired, or already used | Request new federated identity challenge |
IDAAS-RES-3009 | 404 | FederatedIdentity not found | No federated identity exists for this tag | Create federated identity via POST /v1/identities/federate |
IDAAS-RES-3010 | 404 | Transaction not found | Transaction reference does not exist | Verify transaction reference |
IDAAS-RES-3011 | 404 | ApplicationWallet not found | Application wallet not auto-provisioned (rare) | Contact support; wallet should auto-create on first transaction |
IDAAS-RES-3012 | 404 | Resource not found | Invalid endpoint path | Verify endpoint path matches API documentation |
Resource Conflicts (4000–4999)
| Code | HTTP | Message | Cause | Resolution |
|---|---|---|---|---|
IDAAS-CONF-4001 | 409 | Tag already exists | Tag with this qualified address already exists globally | Use different tag name in your application |
IDAAS-CONF-4002 | 409 | Your application already has a tag with this name | Tag string already exists in your application | Use different tag name or reuse existing |
IDAAS-CONF-4003 | 409 | Subject already exists with this external ID | Subject with this external ID already exists in app | Use unique external ID or fetch existing subject |
IDAAS-CONF-4004 | 409 | Subject already has an active claim on this tag | Subject is already linked to tag via active claim | Revoke existing claim if you need to re-link |
IDAAS-CONF-4005 | 409 | A pending claim for this subject/tag already exists | Pending consent request for this subject/tag in progress | Wait for pending claim to be approved/denied or revoke it |
Invalid State / Business Logic (5000–5999)
| Code | HTTP | Message | Cause | Resolution |
|---|---|---|---|---|
IDAAS-STATE-5001 | 422 | Cannot claim a disabled tag | Attempting to claim a tag with status = DISABLED | Enable tag via PATCH /v1/tags/{tag}/enable or use active tag |
IDAAS-STATE-5002 | 422 | Subject does not belong to the calling application | Subject is owned by a different application | Use a subject owned by your application |
IDAAS-STATE-5003 | 422 | Consent token has expired | Consent token is past expiration (default: 48 hours) | Request new claim for fresh consent token |
IDAAS-STATE-5004 | 422 | Challenge is no longer valid | Identity link challenge in terminal state (USED, DENIED, EXPIRED) | Request new federated identity challenge |
IDAAS-STATE-5005 | 422 | Only ACTIVE or PENDING claims can be revoked | Attempting to revoke claim in REVOKED or DENIED state | Only active/pending claims can be revoked |
IDAAS-STATE-5006 | 422 | New owner must have an active claim on this tag before transfer | Tag transfer target does not have active claim | Ensure new owner has approved claim before transferring |
IDAAS-STATE-5007 | 409 | Transaction is not in AWAITING_ACCEPTANCE state | Attempting to accept/reject transaction not pending | Check transaction status; only AWAITING_ACCEPTANCE can be acted on |
IDAAS-STATE-5008 | 422 | Transaction has expired | Attempting to accept transaction past expiration time | Transaction cannot be acted on; check with receiver if needed |
Rate Limiting (6000–6999)
| Code | HTTP | Message | Cause | Resolution |
|---|---|---|---|---|
IDAAS-RATE-6001 | 429 | Rate limit exceeded. Retry after [N] seconds. | Too many requests for this endpoint/IP/app | Use Retry-After header for exponential backoff; default limits: 10 req/min on /v1/auth/token, 60 req/min on /v1/transactions, 300 req/min on others |
System / Internal Errors (9000–9999)
| Code | HTTP | Message | Cause | Resolution |
|---|---|---|---|---|
IDAAS-SYS-9001 | 500 | An unexpected error occurred | Unhandled exception in application logic | Retry with exponential backoff; contact support if persistent |
IDAAS-SYS-9002 | 400 | JWE decryption error | JWE payload is malformed or uses wrong encryption parameters | Verify encryption key and payload format |
Error Code Categories
| Category | Code Range | Description |
|---|---|---|
| AUTH | 1000–1999 | Authentication & Authorization failures |
| VALID | 2000–2999 | Request validation & bad input |
| RES | 3000–3999 | Resource not found / lookup failures |
| CONF | 4000–4999 | Resource conflicts & duplicates |
| STATE | 5000–5999 | Invalid state transitions & business logic |
| RATE | 6000–6999 | Rate limiting & throttling |
| SYS | 9000–9999 | System/Internal errors |
errorCode and message.
HTTP status codes
| Code | Meaning | Action |
|---|---|---|
200 / 201 | Success | Process normally |
400 | Validation error | Fix request body; check message for field details |
401 | Missing or invalid token | Re-authenticate with POST /v1/auth/token |
403 | Forbidden — wrong application | Verify you’re using the correct token for this operation |
404 | Resource not found | Check the reference/ID |
409 | Conflict — duplicate or invalid state | e.g. tag already exists, transaction already completed |
429 | Rate limit exceeded | Back off and retry with exponential delay |
500 | Server error | Retry with back-off; contact IDaaS support if persistent |
Validation error example
22. API Reference Summary
Base path for all endpoints:/api/v1
All protected endpoints require:Authorization: Bearer <token>
Applications & Authentication
| Method | Path | Auth | Description |
|---|---|---|---|
POST | /v1/applications | Public | Register application — appHandle required; secrets returned once |
GET | /v1/applications | Bearer | List all applications |
GET | /v1/applications/{id} | Bearer | Get application by UUID (includes appHandle) |
PATCH | /v1/applications/{id}/deactivate | Bearer | Deactivate application + blacklist tokens |
POST | /v1/auth/token | Public | Exchange credentials for JWT (rate: 10/min/IP) |
POST | /v1/applications/keys/rotate | Bearer | Rotate EC keypair — new private key returned once |
POST | /v1/applications/webhook-secret/rotate | Bearer | Rotate webhook signing secret — new secret returned once |
Encryption Key Management
| Method | Path | Auth | Description |
|---|---|---|---|
GET | /v1/keys/idaas | Public | IDaaS public key (JWK + Base64 DER) |
Subjects
| Method | Path | Auth | Description |
|---|---|---|---|
POST | /v1/subjects | Bearer | Create a subject (user) |
GET | /v1/subjects | Bearer | List all subjects for calling app |
GET | /v1/subjects/{id} | Bearer | Get subject by UUID |
GET | /v1/subjects/{id}/tags | Bearer | List tags owned by a subject |
Tags
| Method | Path | Auth | Description |
|---|---|---|---|
POST | /v1/tags | Bearer | Create a tag (qualified address localTag@appHandle derived automatically) |
GET | /v1/tags | Bearer | List tags for calling app |
GET | /v1/tags/{tag} | Public | Resolve tag by bare name or qualified address tag@appHandle (cached 30 s; 409 if bare name is ambiguous) |
PATCH | /v1/tags/{tag}/disable | Bearer | Disable a tag |
POST | /v1/tags/{tag}/transfer | Bearer | Transfer tag ownership |
POST | /v1/tags/{tag}/claims | Bearer | Request a cross-app claim |
GET | /v1/tags/{tag}/claims | Bearer | List claims on a tag |
POST | /v1/tags/{tag}/federation | Bearer | Initiate federation |
GET | /v1/tags/{tag}/federation | Bearer | Get federation info |
Transactions
| Method | Path | Auth | Description |
|---|---|---|---|
POST | /v1/transactions | Bearer | Initiate payment; senderTag/receiverTag accept bare name or qualified address (rate: 60/min/app) |
GET | /v1/transactions/{reference} | Bearer | Get transaction + webhook delivery status |
GET | /v1/transactions/sent | Bearer | Paginated sent transactions |
GET | /v1/transactions/received | Bearer | Paginated received transactions |
GET | /v1/transactions/pending | Bearer | Paginated acceptance queue |
POST | /v1/transactions/{reference}/accept | Bearer | Accept (receiver app only) |
POST | /v1/transactions/{reference}/reject | Bearer | Reject (receiver app only) |
Wallet & Ledger
| Method | Path | Auth | Description |
|---|---|---|---|
POST | /v1/wallet | Bearer | Provision wallet (idempotent) |
GET | /v1/wallet | Bearer | Get wallet balance |
GET | /v1/wallet/statement | Bearer | Paginated ledger statement |
Settlement
| Method | Path | Auth | Description |
|---|---|---|---|
GET | /v1/settlement | Bearer | List settlement batches (paginated) |
GET | /v1/settlement/{id} | Bearer | Get batch by UUID |
GET | /v1/settlement/date/{date} | Bearer | Get batch by date (YYYY-MM-DD) |
POST | /v1/settlement/run | Bearer | Manually trigger settlement |
Webhooks
| Method | Path | Auth | Description |
|---|---|---|---|
GET | /v1/transactions/{reference}/webhooks | Bearer | All webhook records for a transaction |
GET | /v1/transactions/{reference}/webhooks/receiver | Bearer | RECEIVER webhook record |
GET | /v1/transactions/{reference}/webhooks/sender | Bearer | SENDER webhook record |
GET | /v1/webhooks/{id} | Bearer | Single webhook record by UUID |
GET | /v1/webhooks/failed | Bearer | All FAILED webhooks for calling app |
Consent (Public)
| Method | Path | Auth | Description |
|---|---|---|---|
GET | /v1/consent/{token} | Public | View consent request details |
POST | /v1/consent/{token}/approve | Public | Approve claim |
POST | /v1/consent/{token}/deny | Public | Deny claim |
Claims
| Method | Path | Auth | Description |
|---|---|---|---|
POST | /v1/claims/{id}/revoke | Bearer | Revoke an active claim |
Federation
| Method | Path | Auth | Description |
|---|---|---|---|
POST | /v1/federation/verify/{challenge} | Bearer | Verify federation challenge |
Actuator
| Path | Auth | Description |
|---|---|---|
/actuator/health | Public | Health check (DB + Redis) |
/actuator/info | Public | App info |