CFA Lookup API¶
The CFA Lookup API lets approved partner systems confirm a Clearing and Forwarding Agent by exact identifier within a specified association.
Both versions support one exact lookup at a time. They do not return association-wide member lists. V1 remains backward compatible; V2 adds separately authorized contact, membership lifecycle, restriction context, current disciplinary standing, and association-account financial standing.
Environments¶
| Environment | Base URL | Token issuer |
|---|---|---|
| AWS preproduction | https://api.preprod.taffa.cfhub.net/core |
https://keycloak.preprod.taffa.cfhub.net/realms/cfhub |
| VPS | https://api.taffa.cfhub.net/core |
https://keycloak.taffa.cfhub.net/realms/cfhub |
| Production | https://api.clearingagent.co.tz/core |
https://auth.clearingagent.co.tz/realms/cfhub |
TAFFA will confirm the active base URL, token issuer, client_id, and client_secret during onboarding.
Authentication¶
Partners authenticate with a confidential Keycloak client using the OAuth2 client_credentials grant.
- V1 requires
PARTNER_CFA_LOOKUP. - V2 requires both
PARTNER_CFA_LOOKUPandPARTNER_CFA_CONTACT_READ. TAFFA grants the contact role only after approving the integration's purpose and data handling. - V2 includes the optional
disciplinaryobject only when the token also hasPARTNER_CFA_DISCIPLINARY_READ. A token without this role may still call V2, but the field is omitted rather than returned asnull. - V2 includes the optional
financialStandingobject only when the token also hasPARTNER_CFA_FINANCIAL_READ. It is omitted without that role. This role does not grant invoice, amount, payment-reference, or bank-detail access.
curl -X POST "$ISSUER_URL/protocol/openid-connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=$CLIENT_ID" \
-d "client_secret=$CLIENT_SECRET"
Use the returned access_token as a bearer token. Cache the token until it is close to expiry. Do not send the client secret to the lookup endpoint.
Lookup Request¶
POST /api/v1/cfa/lookup or POST /api/v2/cfa/lookup
| Field | Required | Description |
|---|---|---|
associationCode |
Yes | Association code. TAFFA and TACAS are supported when their registry data is available. |
lookupType |
Yes | One of TIN, ASSOCIATION_MEMBERSHIP_NUMBER, LICENSE_NUMBER, CERTIFICATE_ID, REGISTRATION_NUMBER. |
identifier |
Yes | Exact identifier value. TIN is normalized to digits only. Other values are matched case-insensitively after trimming. |
Example:
curl -X POST "$BASE_URL/api/v1/cfa/lookup" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Request-ID: partner-generated-id" \
-d '{"associationCode":"TAFFA","lookupType":"TIN","identifier":"104006787"}'
Membership-number lookup:
{
"associationCode": "TAFFA",
"lookupType": "ASSOCIATION_MEMBERSHIP_NUMBER",
"identifier": "TAFFA-0001"
}
Interactive API Reference¶
Scalar provides the OpenAPI schema, request examples, bearer-token authorization, and an interactive client:
- AWS preproduction:
https://api.preprod.taffa.cfhub.net/core/api-docs/cfa - VPS:
https://api.taffa.cfhub.net/core/api-docs/cfa - Production:
https://api.clearingagent.co.tz/core/api-docs/cfa
The machine-readable partner specification is at /v3/api-docs/cfa-partner under the same base URL.
Successful Response¶
V1 returns regulatory status and identifiers only. It does not expose company email, phone, address, owners, users, or internal profile fields.
{
"success": true,
"message": "CFA lookup completed",
"data": {
"lookup": {
"requestedType": "TIN",
"requestedValue": "104006787",
"matchedType": "TIN",
"matchedValue": "104006787"
},
"cfa": {
"companyName": "Example Clearing Agent Ltd",
"tin": "104006787",
"registrationNumber": "REG-001",
"businessRegistrationNumber": "BRELA-001",
"active": true,
"memberStatus": "ACTIVE",
"approvalStatus": "APPROVED",
"association": {
"code": "TAFFA",
"name": "TAFFA",
"membershipNumber": "TAFFA-0001",
"membershipType": "ordinary"
},
"license": {
"number": "LIC-001",
"status": "ACTIVE",
"expiresAt": "2026-12-31T23:59:59"
},
"consignmentRegistrationRestricted": false
}
},
"errorCode": null,
"timestamp": "2026-07-02T12:00:00"
}
V2 response¶
V2 adds the stable company identifier, approved primary company contact, membership validity dates, restriction explanation, and—when separately authorized—current disciplinary and association-account financial standing. It still does not expose addresses, owners, users, internal evidence, private notes, invoice details, amounts, bank details, or association-wide records.
{
"success": true,
"message": "CFA lookup completed",
"data": {
"lookup": {
"requestedType": "TIN",
"requestedValue": "999900001",
"matchedType": "TIN",
"matchedValue": "999900001"
},
"cfa": {
"companyId": "00000000-0000-4000-8000-000000000001",
"companyName": "TCAMS Synthetic CFA Demo Ltd",
"tin": "999900001",
"registrationNumber": "DEMO-REG-0001",
"businessRegistrationNumber": "DEMO-BRELA-0001",
"active": true,
"memberStatus": "ACTIVE",
"approvalStatus": "APPROVED",
"contact": {
"email": "cfa-demo@example.invalid",
"phone": "+255700000001"
},
"association": {
"code": "TAFFA",
"name": "TAFFA",
"membershipNumber": "TAFFA-DEMO-0001",
"membershipType": "ordinary",
"membershipValidFrom": "2026-01-01",
"membershipValidTo": "2026-12-31"
},
"license": {
"number": "DEMO-LIC-0001",
"status": "ACTIVE",
"expiresAt": "2026-12-31T23:59:59"
},
"consignmentRegistrationRestricted": false,
"consignmentRestrictionReason": null,
"consignmentRestrictionSetAt": null,
"consignmentRestrictionSource": null,
"disciplinary": {
"flagged": false,
"disciplinaryStatus": "CLEAN",
"reason": null,
"reference": null,
"effectiveFrom": null,
"effectiveTo": null,
"activeActionCount": 0
},
"financialStanding": {
"status": "CLEAR",
"accountInGoodStanding": true,
"outstandingInvoiceCount": 0,
"overdueInvoiceCount": 0,
"oldestOutstandingDueAt": null,
"lastSettledAt": "2026-06-26T10:30:00",
"asOf": "2026-07-16T12:00:00"
}
}
},
"errorCode": null,
"timestamp": "2026-07-16T12:00:00"
}
The 999900001, TAFFA-DEMO-0001, and DEMO-LIC-0001 identifiers are reserved synthetic fixtures in preproduction only. They must never be inserted into production.
V2 responses send Cache-Control: no-store and Pragma: no-cache. The default rate limit is 60 requests per minute per OAuth client; use X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, and Retry-After response headers for client-side backoff.
Current Disciplinary Standing¶
The disciplinary object is a derived current standing, not a complete disciplinary history. It is present only for clients granted PARTNER_CFA_DISCIPLINARY_READ.
An action contributes to the current standing only when all of the following are true:
- its lifecycle is
ACTIVEorUNDER_APPEAL; - it is marked for external lookup visibility;
- its effective time has started and its expiry time has not passed;
- it has not been resolved; and
- enforcement has not been stayed during an appeal.
Resolved, cancelled, expired, future, draft, hidden, and stayed actions remain in the association's audit history but do not flag the company. If several actions are current, the API returns the highest-priority standing in this order: REVOKED, SUSPENDED, FINE, WARNING. activeActionCount reports the number of current enforceable actions.
| Field | Values / meaning |
|---|---|
flagged |
true only when one or more current enforceable actions exist. |
disciplinaryStatus |
CLEAN, WARNING, FINE, SUSPENDED, or REVOKED. |
reason |
Association-approved public reason for the highest-priority current action; never internal case notes or evidence. |
reference |
Public disciplinary reference for the highest-priority current action. |
effectiveFrom, effectiveTo |
Effective window for the returned action. effectiveTo may be null. |
activeActionCount |
Total current enforceable actions. |
Example flagged response fragment:
{
"disciplinary": {
"flagged": true,
"disciplinaryStatus": "SUSPENDED",
"reason": "Membership suspended pending resolution of an active compliance finding",
"reference": "DISC-2026-A1B2C3D4",
"effectiveFrom": "2026-07-16T09:00:00",
"effectiveTo": null,
"activeActionCount": 1
}
}
When the action is resolved, a subsequent lookup returns flagged: false and disciplinaryStatus: CLEAN; the historical action is not exposed through the partner lookup.
Association Financial Standing¶
The optional financialStanding object answers a narrow question: is the CFA's account with the association currently clear? It is present only for clients granted PARTNER_CFA_FINANCIAL_READ.
This is association billing standing, not a statement about the CFA's commercial solvency or the financial health of its customer business. The calculation includes only association-owned charges such as membership, fines, training, and LMS fees. Invoices issued by the CFA to its own customers are explicitly excluded, even if their wording resembles an association charge.
| Field | Values / meaning |
|---|---|
status |
CLEAR when no association invoice is outstanding; PAYMENT_DUE when payment is outstanding but not overdue; OVERDUE when at least one association invoice is past due; UNKNOWN when the source ledger cannot be read reliably. |
accountInGoodStanding |
true only for CLEAR, false for PAYMENT_DUE or OVERDUE, and null for UNKNOWN. |
outstandingInvoiceCount |
Count of association-owned invoices awaiting full settlement; null for UNKNOWN. |
overdueInvoiceCount |
Count of those outstanding invoices that are explicitly overdue or past their due date; null for UNKNOWN. |
oldestOutstandingDueAt |
Earliest due date among current outstanding association invoices, when available. |
lastSettledAt |
Most recent settlement time for an association-owned invoice, when available. |
asOf |
Time at which TCAMS derived the standing. |
Example overdue response fragment:
{
"financialStanding": {
"status": "OVERDUE",
"accountInGoodStanding": false,
"outstandingInvoiceCount": 2,
"overdueInvoiceCount": 1,
"oldestOutstandingDueAt": "2026-07-01T00:00:00",
"lastSettledAt": "2026-06-20T10:30:00",
"asOf": "2026-07-16T12:00:00"
}
}
The object never contains invoice numbers, categories, line items, amounts, balances, currencies, payment references, settlement-account details, or bank data. A CLEAR standing means only that TCAMS found no current outstanding association-owned invoice; it does not erase historical invoices.
Errors¶
| HTTP status | errorCode |
Meaning |
|---|---|---|
400 |
INVALID_LOOKUP_REQUEST |
Required field is missing or lookupType is not supported. |
401 |
UNAUTHORIZED |
Token is missing, expired, or invalid. |
403 |
FORBIDDEN |
Token is valid but lacks a role required by the selected API version. |
404 |
CFA_NOT_FOUND |
No CFA matched the association and identifier. |
429 |
CFA_LOOKUP_RATE_LIMITED |
The V2 per-client rate limit was exceeded. Retry after the number of seconds in Retry-After. |
500 |
CFA_LOOKUP_FAILED |
TAFFA could not complete the lookup. |
Example not found response:
{
"success": false,
"message": "No CFA matched the supplied association and identifier.",
"data": null,
"errorCode": "CFA_NOT_FOUND",
"timestamp": "2026-07-02T12:00:00"
}
Audit And Support¶
TAFFA logs every lookup for operational audit. The audit record includes request id, partner client id, token subject, source IP, forwarded IP headers, user agent, association code, lookup type, normalized identifier hash, outcome, HTTP status, matched company id where available, response source, error code, timestamps, elapsed time, redacted request JSON, and a sanitized response. V2 email, phone, public disciplinary reason, and the complete financial-standing object are never persisted in lookup audit JSON.
Partners should send X-Request-ID on every call and include that value when asking TAFFA support to investigate a lookup.
Credential handling:
- Store
client_secretonly in a secure secret manager. - Rotate credentials when requested by TAFFA or after suspected exposure.
- Never log bearer tokens or client secrets in partner systems.
- Do not share credentials across environments.