ENHANCED INTEGRATION — OPTIONAL WORKFLOW

Individuals, passports and linked company cases

Existing integrations can continue using their existing keys, requests and webhook receivers. Use this workflow only after your organisation administrator enables enhanced verification in Settings. The familiar API reference remains below.

View the previous integration guide

What has changed?

AreaExisting integrationOptional enhanced integration
SubmissionPOST submit.php remains available.POST v2/cases.php supports create, attach_person, finalize and resubmit actions.
Company peopleExisting company fields and document categories.Each person receives a separate case number; link their case to the company case.
IdentityExisting personal profile rules.Select sa_id or passport. Passport applicants supply lawful-status evidence; work/activity permission is assessed only when business_activity is explicitly requested.
ReuseExisting full-check behaviour.Organisation-configurable periods and screening preferences. Explicit full re-verification overrides reuse.
ResultsExisting stable JSON structure.The same structure by default. Settings can enable one additional enhanced_verification section.
ChargesExisting pricing.The company and each linked person have separate ledger charges. Reuse avoids repeating the full document-verification base charge.

Before writing code

  1. Keep your API key on your server. Use X-API-Key on every request.
  2. Enable enhanced verification in the organisation’s Settings. Leave enhanced JSON off if your receiver expects exactly the old keys.
  3. Use a test organisation key first. Test and live cases cannot be linked or reused across environments.
  4. Configure the existing signed completion webhook. Each person has their own completion event; use case.case_id to identify it.

Create an individual or director

Send multipart/form-data to https://api.ficasure.com/api/v2/cases.php. Let your HTTP library set the multipart boundary. Text fields are action=create, a unique request_id, and payload containing a JSON object. Reuse the same request_id when retrying the same creation request to avoid creating another case.

{
  "external_client_id": "your-person-482",
  "source_app": "your-server",
  "application_type": "personal",
  "full_name": "Example Person",
  "identification_type": "passport",
  "passport_number": "EXAMPLE123",
  "passport_issuing_country": "GB",
  "date_of_birth": "1985-02-10",
  "residential_address": "Your applicant's complete address",
  "mobile_number": "+27000000000",
  "email": "person@example.com",
  "verification_purpose": "personal",
  "terms_accepted": true,
  "force_full_reverification": false,
  "reuse_consent": false,
  "name_matching_level": "1"
}

For a South African ID, use identification_type=sa_id and id_number. For passports, the issuing country is mandatory for reliable matching because passport numbers are not globally unique.

Individual file fields are identity_document[], proof_of_address[], selfie[], supporting_document[], and, for passport applicants, lawful_status_document[]. A category accepts three files per person, except supporting_document, which accepts five. PDF and image handling follows the existing upload restrictions.

Personal passport verification does not demand a work visa. Supply evidence of the relevant lawful status, such as a visa, permit, permanent-residence evidence or applicable exemption evidence. FICA completion is an onboarding assessment under the organisation’s rules; it is not a guarantee that a particular bank will open an account.

Explicit business activity

When permission for a specific activity must be checked, set verification_purpose=business_activity and provide intended_activity describing the role, business, employer and activity. The document’s conditions must support that specific activity. The words “work visa” alone do not establish permission for every business activity.

Additional supported evidence fields are passport_expiry_date, lawful_status_type, lawful_status_reference, lawful_status_expiry_date, permitted_activity and permission_conditions. Submitted values do not replace evidence extracted from the documents. Dates use YYYY-MM-DD.

Create and assess a company with any number of people

  1. Create each person as above. Save their returned case_id alongside your own person reference. Each person uploads their own documents in their own request.
  2. Create the company with action=create, a new request_id and the existing company application_type and fields. Upload the company documents using the categories for that entity type. The response has status=draft.
  3. Attach each person using the fields below. There is no shared three-person or three-document limit on the company. Large companies can upload one person at a time.
  4. Wait for the person completion webhooks. Resolve any missing-information requests. Each individual can be cleared separately.
  5. Send action=finalize and the company case_id. A 409 response means a person is still processing or the company is already submitted; inspect the message before retrying.
  6. Receive the company’s completion webhook. Multiple associated people require final human review. A sole associated person and the company can pass automatically when all requirements pass.
POST /api/v2/cases.php
Content-Type: application/x-www-form-urlencoded
X-API-Key: your organisation key

action=attach_person
case_id=COMPANY_CASE_ID
person_case_id=PERSON_CASE_ID
person_reference=director-1
role=director

Allowed role values: owner, director, beneficial_owner, partner, trustee, authorised_person, member, executor, controller. Include every required person found in the company’s evidence. Repeating the same person_reference is rejected; it must identify one person within that company.

Missing information and resubmission

decision.status=needs_information asks the applicant for evidence; it does not by itself request human approval. Display customer_message and requirements.outstanding. To update the same case, POST action=resubmit, case_id, a payload containing corrected fields, and replacement document categories to v2/cases.php. Each uploaded category replaces that category’s active evidence for the new assessment; earlier evidence remains in the audit history. Submit all files you want retained in a replaced category. The revised case is fully assessed again.

Reusable verification: company-controlled periods

Periods are variables set in Settings for the requesting organisation. Period 2 is the total maximum age from the original full assessment, not extra months added to Period 1.

  • During Period 1, reuse the existing approved information without comparing newly submitted information.
  • During Period 2, compare the information extracted from new evidence with the verified profile. Different file formats or filenames alone do not cause re-verification. Material information changes do.
  • Beyond Period 2, the next submission automatically triggers full verification. There is no background recheck just because a date passes.
  • Previously unapproved results and force_full_reverification=true require full assessment.
  • PEP and sanctions reuse have separate controls for each period. Reused screening is identified as reused, not presented as a fresh provider check.
  • Reusing a result does not reset its original verification date.
  • Cross-organisation reuse requires participating organisations to enable it and recorded applicant consent. Another company’s case number or documents are not returned to the caller.

Screening flags and company relationships

A screening flag associated with a linked company prevents automatic approval of the linked person and records the company case and reported role for the reviewer. A possible name match or association is a reason to investigate, not proof of wrongdoing. No automatic rejection is based solely on PEP status.

Optional result details

With the Settings switch off, result and webhook JSON retain the existing structure. When enabled, a top-level enhanced_verification object is added. Existing properties are not renamed or moved.

"enhanced_verification": {
  "version": "1.0",
  "people": [
    {"person_reference":"director-1","case_id":123,"role":"director",
     "status":"approved","fica_complete":true,"human_review_required":false,
     "missing_items":[]}
  ],
  "reuse": {"action":"full","original_verified_at":null},
  "missing_items": []
}

Use the existing decision.status, decision.fica_complete, decision.application_access and decision.human_review_required fields for approval logic. A successful HTTP response or completed processing stage does not mean the applicant is approved. Store webhook delivery IDs to deduplicate retries. Continue verifying the existing HMAC signature against the raw request body.

Name matching

Send name_matching_level as "0" for exact names, "1" for small spelling differences/common variations, or "2" for broader variations. These levels apply to both ordinary and enhanced submissions. They do not change the selected test/live environment.

Charges and error handling

Individual checks start from 8 credits and business checks from 12 credits. Final cost depends on the configured entity rate, evidence processed, additional processing and each person checked. Each person has their own case charge, separate from the company charge. Inspect the existing billing section and credit ledger; do not calculate a final invoice from the base price alone.

Handle 401 as an authentication error; 403 as an access error; 404 as a missing/inaccessible case; 409 as a workflow conflict or disabled feature; 422 as invalid input or insufficient credits; and 500/503 as a server failure. Retrying creation must retain request_id. Never retry a completed assessment merely because your browser failed to redirect.

What existing developers need to change

Nothing is required to keep the existing integration running. To use the enhanced company flow, add the new endpoint calls, save separate person case IDs, implement company linking/finalization and handle Needs Information. Only enable enhanced JSON once the receiver can accept the optional section. Do not regenerate your API key or recreate your webhook solely for this update.

COMPLETE INTEGRATION GUIDE

Connect your system to FICA sure

This guide explains the complete integration from account setup to receiving a finished verification. You do not need to know how FICA sure works internally. Your server sends applicant information and evidence, receives a case number, and later receives the completed result by webhook or retrieves it from the result endpoint.

Please sign in to use this area.
Important security rule: every FICA sure API call must come from your server. Never put an API key, client password, or webhook signing secret in browser JavaScript, a mobile application, HTML, a public form, a screenshot, source control, or application logs.

1. How the integration works

1Your serverCollects applicant details and evidence securely.
2FICA sure APIAccepts the case using your organisation API key.
3ProcessingAnalyses evidence, resolves the profile and runs configured screening.
4ResultPushes signed JSON to your webhook; polling remains available.
  1. Obtain a test key and keep it in server-side secret storage.
  2. Call application_types.php to discover the current fields and evidence requirements.
  3. Submit one case to submit.php using multipart/form-data.
  4. Save both your external_client_id and the returned FICA sure case_id.
  5. Wait for the signed completion webhook, or poll status.php.
  6. Read the final stable contract from v1/result.php.

The submit request is asynchronous. A successful HTTP 202 means the case was accepted and queued. It does not mean the verification has finished.

Small glossary

TermPlain-language meaning
API base URLThe shared beginning of every FICA sure API address, such as https://api.ficasure.com/api/.
EndpointOne specific API function appended to the base URL, such as submit.php.
RequestInformation your server sends to an endpoint.
ResponseThe HTTP status and JSON information FICA sure sends back.
JSONA structured text format made of named fields, objects and lists.
HeaderExtra request information. Your API key is sent in the X-API-Key header.
Multipart form-dataThe request format used to send ordinary fields and actual files together.
WebhookA URL on your server that FICA sure calls automatically with a completed result.
EnvironmentEither test or live. The API key—not a submitted field—selects it.

2. Quick start

What you need

  • The public API base URL, normally https://api.ficasure.com/api/.
  • An active FICA sure organisation.
  • Test credits and a test API key while developing.
  • A server-side application capable of HTTPS and multipart file uploads.
  • A public HTTPS webhook URL if completed results must be pushed automatically.

Three-request connection test

Replace YOUR_CLIENT_KEY and run these requests in order. Receiving JSON from all three confirms the URL, key, environment and result access are working.

# 1. Confirm the key and balance
curl "https://api.ficasure.com/api/account.php" 
  -H "X-API-Key: YOUR_CLIENT_KEY" 
  -H "Accept: application/json"

# 2. Load the current application requirements
curl "https://api.ficasure.com/api/application_types.php" 
  -H "X-API-Key: YOUR_CLIENT_KEY" 
  -H "Accept: application/json"

# 3. After submitting a case, retrieve its status
curl "https://api.ficasure.com/api/status.php?case_id=123" 
  -H "X-API-Key: YOUR_CLIENT_KEY" 
  -H "Accept: application/json"
URL rule: a hosted client cannot call localhost, 127.0.0.1, a Laragon path, or host.docker.internal on your development computer. Production integrations must call the public HTTPS API URL.

3. Test the integration with Postman

The supplied collection contains registration, key management, account, requirements, submission, status and result requests. It automatically saves a returned case_id after a successful submission.

  1. Open Postman and choose Import.
  2. Import FICA-sure.postman_collection.json.
  3. Open the collection’s Variables tab.
  4. Set base_url to https://api.ficasure.com/api without adding an endpoint filename.
  5. Paste the test key into api_key. Never share an exported collection containing a real key.
  6. Set environment to test. This variable is only used by key-management requests; the actual API key chooses the environment for authenticated calls.
  7. Run Get account and credit balance, then Get application types and requirements.
  8. Open Submit personal verification. In Body → form-data, replace the sample values. Enable each required file row and select a local file.
  9. Give every intended case a new external_client_id.
  10. Send the request. HTTP 202 is the normal queued response and the collection saves its case_id.
  11. Run Get status by case ID until it is no longer queued/running, then run Get stable v1 result by case ID.

Several files in Postman

Duplicate the form-data row and keep the key exactly the same. Three address files must all use proof_of_address[]. Do not rename them to proof_of_address_1 or document2.

Common Postman mistake

Do not manually add Content-Type: multipart/form-data. Postman creates the required multipart boundary when Body is set to form-data. Add only X-API-Key and Accept: application/json.

4. Organisations, keys and environments

Cases, credits, keys and webhook settings are organisation-scoped. A customer key cannot retrieve another organisation’s case. An unauthorised lookup returns 404 Not found instead of confirming whether that case exists.

Test key

Uses the test balance. Test cases remain separate from live cases and use the local PEP sandbox where applicable. They do not consume live PEP supplier quota.

Live key

Uses the live balance and production screening configuration. A live case cannot silently become a simulated local PEP result.

Do not send an environment field with a verification. FICA sure reads the environment from the key. Changing a form label to “live” while still sending a test key does not create a live case.

Recommended key setup

  1. Sign in to FICA sure.
  2. Open API Integrations.
  3. Under Integration Access, enter the account password.
  4. Create the test or live key.
  5. Copy it immediately into server-side secret storage. It cannot be displayed again.

Alternative: register and manage keys by API

POST /api/register.php
Content-Type: application/json

{
  "organization_name": "Client X",
  "full_name": "Jane Doe",
  "email": "jane@example.com",
  "mobile": "+27110000000",
  "password": "at-least-10-characters"
}

Registration returns HTTP 201 with status pending. An administrator must activate the organisation and allocate credits before a key can be issued.

POST /api/credentials.php
Content-Type: application/json

{
  "email": "jane@example.com",
  "password": "CLIENT_PASSWORD",
  "environment": "test",
  "action": "issue"
}
ActionUseEffect
issueCreate the first key for an environment.Returns the raw key once. Returns 409 if one exists.
rotateReplace an existing key.The previous key stops working immediately.
revokeRemove access for that environment.No replacement key is returned.
FICA_SURE_API_BASE_URL=https://api.ficasure.com/api/
FICA_SURE_API_KEY=the_key_returned_once_by_fica_sure
FICA_SURE_WEBHOOK_SECRET=the_webhook_secret_returned_once

5. Endpoint reference

Method and endpointPurposeAuthenticationSuccess
POST register.phpRegister a pending organisation.No API key201
POST credentials.phpIssue, rotate or revoke a key.Email/password in body201/200
GET account.phpConfirm organisation, environment and credits.Organisation key200
GET application_types.phpGet current fields and evidence requirements.Organisation key200
POST submit.phpCreate and queue a verification.Organisation key202
GET status.phpGet lightweight processing progress.Same environment key200
GET v1/result.phpGet the stable completed-result contract.Same environment key200
X-API-Key: YOUR_CLIENT_KEY
Accept: application/json

6. Application types, fields and evidence

Always load requirements first

GET /api/application_types.php
X-API-Key: YOUR_CLIENT_KEY

This response is authoritative. Administrators can change whether a supported field or document is required, so do not permanently hard-code one personal form.

ModeWhat your form should do
requiredCollect it. The case cannot be FICA-complete without a usable/resolved item.
optionalAllow it without blocking submission when absent.
edd_onlyCollect it when enhanced due diligence applies.
ignoreDo not show or send it for that application type.

Current application keys include personal, sole_proprietor, partnership, close_corporation, private_company, personal_liability_company, public_company, trust, estate, association_society_club_trade_union, npc_npo, and merchant_card_acceptance. Use the returned key, not its visible label.

Request metadata

FieldRequired?Meaning
external_client_idYesYour unique, non-secret reference for this case. Store it.
application_typeRecommendedA key from application_types.php. Validate it before sending.
source_appNoA short identifier for your server, such as client-portal.
application_referenceNoYour business reference. FICA sure generates one if omitted.
personal_profile_client_idLinked business flowsLinks a business application to a previously checked personal profile where configured.
name_matching_levelNoControls name-list comparison for this case.

Evidence rules

  • Use multipart/form-data.
  • File keys must exactly match a non-ignored document_modes key.
  • Supported content types are JPEG, PNG, WebP and PDF. The server checks the real MIME type.
  • The current limit is 15 MB per file. Hosting/proxy limits may be lower.
  • Send up to three files per configured evidence type and up to five supporting_document[] files.
  • Every repeated file is preserved and assessed separately.

Correct:

proof_of_address[] = address-letter.pdf
proof_of_address[] = municipal-account.jpg
proof_of_address[] = property-photo.webp

Incorrect:

proof_of_address_1
proofOfAddress
address_document
document2

7. Exact, Fuzzy 1 and Fuzzy 2 name checks

The website uses plain-language labels. API integrations select the same behaviour by sending name_matching_level with each case. The choice affects local FIC/UN name comparison and the configured PEP name search. It does not replace evidence, identity or date-of-birth checks.

ValueWebsite descriptionTechnical nameBehaviour
0Closest spelling onlyExactCompares the normalised spelling. Letter case, spacing, punctuation and accents are normalised.
1Allow small differencesFuzzy 1Includes small spelling differences and common name variations. This is the recommended default.
2Search more broadlyFuzzy 2Includes broader spelling and name-order variations and may return more possible matches for review.
# Exact / closest spelling only
-F "name_matching_level=0"

# Fuzzy 1 / allow small differences
-F "name_matching_level=1"

# Fuzzy 2 / search more broadly
-F "name_matching_level=2"

If omitted or invalid, the configured standard comparison is used. The final result confirms it in case.name_matching.level and case.name_matching.description.

8. Submit a verification

curl -X POST "${FICA_SURE_API_BASE_URL}submit.php" 
  -H "X-API-Key: ${FICA_SURE_API_KEY}" 
  -H "Accept: application/json" 
  -F "external_client_id=client-x-000123" 
  -F "source_app=client-x-website" 
  -F "application_type=personal" 
  -F "application_reference=ONBOARDING-000123" 
  -F "name_matching_level=1" 
  -F "full_name=Jane Doe" 
  -F "id_number=0000000000000" 
  -F "date_of_birth=1990-01-31" 
  -F "nationality=ZA" 
  -F "email=jane@example.com" 
  -F "mobile_number=+27110000000" 
  -F "terms_accepted=1" 
  -F "identity_document[]=@/secure/path/id.jpg" 
  -F "proof_of_address[]=@/secure/path/address.pdf" 
  -F "proof_of_address[]=@/secure/path/municipal-account.jpg" 
  -F "selfie[]=@/secure/path/selfie.jpg"

Replace every sample value and only send fields permitted by the selected application type.

Successful queued response

HTTP/1.1 202 Accepted
Content-Type: application/json

{
  "success": true,
  "case_id": 123,
  "status": "queued",
  "environment": "test",
  "credits_reserved": 8
}

Save case_id, external_client_id, and environment immediately. If the connection breaks after sending, query by the same external ID before retrying so an accidental duplicate is not created.

Minimal PHP example

$post = [
  'external_client_id' => 'client-x-000123',
  'application_type' => 'personal',
  'name_matching_level' => '1',
  'full_name' => 'Jane Doe',
  'identity_document[0]' => new CURLFile('/secure/id.jpg', 'image/jpeg', 'id.jpg'),
];
$curl = curl_init(getenv('FICA_SURE_API_BASE_URL') . 'submit.php');
curl_setopt_array($curl, [
  CURLOPT_POST => true,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_TIMEOUT => 90,
  CURLOPT_HTTPHEADER => ['X-API-Key: '.getenv('FICA_SURE_API_KEY'), 'Accept: application/json'],
  CURLOPT_POSTFIELDS => $post,
]);
$body = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if ($body === false) throw new RuntimeException(curl_error($curl));
$response = json_decode($body, true, 512, JSON_THROW_ON_ERROR);
if ($status !== 202) throw new RuntimeException($response['error'] ?? 'Submission failed');

9. Progress and stable results

GET /api/status.php?case_id=123
X-API-Key: YOUR_CLIENT_KEY

# Your identifier also works:
GET /api/status.php?external_client_id=client-x-000123
Processing valueMeaningAction
queuedWaiting for a worker.Wait and check again.
runningAutomated checks are active.Wait and check again.
completedAutomated processing finished.Retrieve v1 result and inspect the decision.
waiting_human_responseAn administrator must decide.Do not approve. Wait for review.
failedA technical stage stopped.Do not approve; show a safe pending/error message.

Stable v1 contract

GET /api/v1/result.php?case_id=123
X-API-Key: YOUR_CLIENT_KEY

# Or:
GET /api/v1/result.php?external_client_id=client-x-000123
X-API-Key: YOUR_CLIENT_KEY

Use this versioned endpoint in application code. It returns schema.name = cris_fica_result and schema.version = 1.0, with stable profile/document slots for every application type.

{
  "schema": {"name": "cris_fica_result", "version": "1.0"},
  "success": true,
  "case": {
    "case_id": 123,
    "external_client_id": "client-x-000123",
    "application_type": "personal",
    "name_matching": {"level": "1", "description": "Small spelling differences and common name variations"}
  },
  "processing": {"status": "completed", "stages": []},
  "decision": {"status": "approved", "identity_verified": true, "fica_complete": true, "application_access": "allow", "human_review_required": false},
  "completion": {},
  "profile": {"fields": []},
  "requirements": {"outstanding": [], "mismatches": [], "review_flags": []},
  "documents": [],
  "screening": {},
  "human_review": {},
  "customer_message": "",
  "usage": {},
  "billing": {}
}

Making an access decision

  • HTTP 200 only means the result was retrieved.
  • Inspect decision.status, decision.fica_complete, decision.application_access, and decision.human_review_required together.
  • If human review is required or screening is not clear, never auto-approve.
  • Show customer_message to the customer. Do not expose raw provider data or administrator diagnostics.

10. Receive completed results by webhook

A webhook means FICA sure calls your server when the result changes, so you do not need to poll continuously.

  1. Create a public endpoint, for example https://client.example.com/webhooks/fica-sure.
  2. Open API Integrations → Integration Access → Completion webhook.
  3. Enter the HTTPS URL and enable delivery.
  4. Confirm the account password and save.
  5. Copy the signing secret immediately; it cannot be shown again.

Production endpoints must use HTTPS, resolve publicly and cannot point to private/reserved addresses or include embedded credentials. A hosted server cannot call localhost on your computer.

HeaderMeaning
X-FICA-Signaturesha256= plus the HMAC-SHA256 of the exact raw body.
X-FICA-Eventcase.completed, case.failed, or case.reviewed.
X-FICA-Delivery-IDA unique delivery ID. Store it to handle duplicates safely.
X-FICA-Case-IDThe numeric case ID.

PHP receiver

$raw = file_get_contents('php://input');
$received = $_SERVER['HTTP_X_FICA_SIGNATURE'] ?? '';
$expected = 'sha256=' . hash_hmac('sha256', $raw, getenv('FICA_SURE_WEBHOOK_SECRET'));
if (!hash_equals($expected, $received)) {
  http_response_code(401);
  exit;
}
$deliveryId = $_SERVER['HTTP_X_FICA_DELIVERY_ID'] ?? '';
// If this ID was processed before, return 204 without processing twice.
$result = json_decode($raw, true, 512, JSON_THROW_ON_ERROR);
// Save or queue the result, then acknowledge quickly.
http_response_code(204);

Node.js/Express receiver

import crypto from 'node:crypto';
app.post('/webhooks/fica-sure', express.raw({type: 'application/json'}), (req, res) => {
  const expected = 'sha256=' + crypto.createHmac('sha256', process.env.FICA_SURE_WEBHOOK_SECRET).update(req.body).digest('hex');
  const received = req.get('X-FICA-Signature') || '';
  const valid = expected.length === received.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received));
  if (!valid) return res.sendStatus(401);
  const result = JSON.parse(req.body.toString('utf8'));
  // Store idempotently using X-FICA-Delivery-ID.
  return res.sendStatus(204);
});

Return 2xx quickly. FICA sure records the attempt and HTTP status. Keep status/result polling as recovery if your endpoint was unavailable during delivery.

11. Credits

GET /api/account.php
X-API-Key: YOUR_CLIENT_KEY

The key selects the test or live balance. Read credits.balance, credits.reserved, and credits.available. A case reserves an estimate when accepted; successful processing finalises it; technical failure releases/refunds it. Insufficient available credit rejects the submission.

12. Errors and troubleshooting

StatusMeaningCheck
400Malformed request.Method and body encoding.
401Credential rejected.Correct key/password, active account and no extra spaces.
403Wrong credential type or permission.Use an organisation key, not the WordPress connector key.
404Endpoint/case not found or isolated.URL, case ID, organisation and same test/live key.
405Wrong HTTP method.Use POST where documented.
409Key already exists.Rotate only when intentionally replacing it.
422Request cannot be accepted.Fields, application/document keys, file count/type/size, credits and account state.
429Too many requests.Back off; avoid tight polling.
500–599Server/upstream issue.Record safe diagnostics and query status before resubmitting.

“FICA API unavailable”

  1. Test from the client server, not only your browser.
  2. Confirm the base URL ends in /api/.
  3. Confirm the response is JSON, not HTML, a hosting login or firewall challenge.
  4. Check DNS, TLS, outbound firewall, ModSecurity and PHP cURL.
  5. Test account.php with the organisation key.

Test case not found with a live key

This is intentional. Use the same environment key that created the case.

Only one uploaded file appears

Use repeated canonical keys with brackets and confirm your library did not overwrite earlier map entries.

Live PEP screening error

Confirm the live organisation key created the case and live screening is enabled with a valid administrator-managed credential. A failed live call is not silently replaced with a local clear result.

Safe diagnostics

Record request time, endpoint, method, HTTP status, external_client_id, returned case_id, environment and a redacted response. Never record the key, password, webhook secret, evidence contents or unredacted personal data.

13. Production checklist

  • Use the public HTTPS API URL.
  • Keep secrets on the server only.
  • Store separate test and live keys.
  • Check account.php and returned environment.
  • Load application_types.php.
  • Honour required, optional, EDD-only and ignored modes.
  • Use a unique external ID per intended case.
  • Choose the name matching level.
  • Send files as repeated canonical multipart keys.
  • Save both identifiers.
  • Treat 202 as queued, not approved.
  • Use v1/result.php.
  • Check decision, completion, access and human review together.
  • Verify webhook signatures over the raw body.
  • Deduplicate delivery IDs.
  • Return 2xx quickly and keep polling as fallback.
  • Handle ambiguous submissions without creating duplicates.
  • Never expose provider identities, supplier responses, secrets or administrator diagnostics.