Mono-Parser
Parser
Get Started

Built for Nigerian Fintechs

Contents
Table of Contents

Webhooks

Mono-Parser pushes events to your registered webhook URL. All webhook payloads share the same envelope shape:

json
{
  "event": "event.name",
  "data": { ... },
  "timestamp": "2026-02-20T22:14:35.703Z"
}
Register your webhook URL in the dashboard under Settings → Webhook URL. Your endpoint must respond with HTTP 2xx within 10 seconds. Failed deliveries are retried with exponential backoff.
EVENTaccount.linked

account.linked

Fired when a bank account is successfully linked via Mono Connect. Enrichment starts automatically after this event — you do not need to do anything yet. Wait for account.enrichment_ready before calling analyze.

json
{
  "event": "account.linked",
  "data": {
    "applicationId": "357ab3ce-55ce-4f73-82c9-dab3136c7885",
    "applicantId":   "54cbd45f-bf8e-4add-8d0c-adb5efe705c1",
    "accountId":     "dbcc78d5-0719-4344-ac0e-e02a1f865bf0",
    "institution":   "Standard Chartered",
    "accountNumber": "0131883461"
  },
  "timestamp": "2026-02-20T22:04:14.123Z"
}
EVENTaccount.enrichment_ready

account.enrichment_ready

Fired per account when income analysis and statement insights have both completed for that account. If your applicant is linking multiple accounts, you will receive this event once per account. When you are done linking all accounts, call /finalize-linking — we will then fire application.ready_for_analysis as your trigger for /analyze.

json
{
  "event": "account.enrichment_ready",
  "data": {
    "accountId":     "dbcc78d5-0719-4344-ac0e-e02a1f865bf0",
    "monoAccountId": "6998da59bdaef66d5e5f3d0d",
    "applicantId":   "54cbd45f-bf8e-4add-8d0c-adb5efe705c1",
    "applicationId": "357ab3ce-55ce-4f73-82c9-dab3136c7885",
    "message":       "Account enrichment complete. You may now submit this applicant for loan analysis."
  },
  "timestamp": "2026-02-20T22:07:55.382Z"
}
If your applicant has only one account, call /finalize-linking immediately after this event. For multi-account flows, wait until all accounts fire this event before finalizing.
EVENTapplication.ready_for_analysis

application.ready_for_analysis

Fired after you have called /finalize-linking and all linked accounts on the application are confirmed enriched. This is your definitive signal to call /analyze. Unlike account.enrichment_ready which fires per account, this fires once per application.

json
{
  "event": "application.ready_for_analysis",
  "data": {
    "applicationId": "357ab3ce-55ce-4f73-82c9-dab3136c7885",
    "applicantId":   "54cbd45f-bf8e-4add-8d0c-adb5efe705c1",
    "accountCount":  1,
    "message":       "All accounts are enriched. You may now submit for analysis."
  },
  "timestamp": "2026-02-20T22:08:10.441Z"
}
EVENTaccount.enrichment_failed

account.enrichment_failed

Fired when an account stays in PENDING enrichment status for more than 20 minutes. This is a cleanup safety net — it means our system could not complete income analysis or statement insights for that account within the expected window.

json
{
  "event": "account.enrichment_failed",
  "data": {
    "accountId":     "dbcc78d5-0719-4344-ac0e-e02a1f865bf0",
    "monoAccountId": "6998da59bdaef66d5e5f3d0d",
    "applicantId":   "54cbd45f-bf8e-4add-8d0c-adb5efe705c1",
    "reason":        "enrichment_timeout",
    "message":       "Account enrichment did not complete within the expected window. Ask the applicant to re-link their bank account to restart the process."
  },
  "timestamp": "2026-02-20T22:40:00.000Z"
}
Ask the applicant to re-link their bank account using POST /api/applications/:id/link-account. This generates a fresh Mono Connect widget URL and restarts the enrichment process for that account.
EVENTapplication.failed

application.failed

Fired when the scoring engine encounters a terminal error it cannot recover from — for example, the applicant has no enriched bank accounts at analysis time, or the analysis service returned an unrecoverable error. The application status is set to FAILED.

json
{
  "event": "application.failed",
  "data": {
    "applicationId": "357ab3ce-55ce-4f73-82c9-dab3136c7885",
    "applicantId":   "54cbd45f-bf8e-4add-8d0c-adb5efe705c1",
    "status":        "FAILED",
    "reason":        "No accounts with completed enrichment available for analysis"
  },
  "timestamp": "2026-02-20T22:15:00.000Z"
}
A failed application cannot be re-analyzed. You will need to call /initiate to create a new application for the same applicant.
EVENTapplication.abandoned

application.abandoned

Fired by the cleanup system when an application is inactive too long. There are two abandonment reasons — check the reason field to distinguish them.

reason: no_link

The applicant did not link any bank account within 24 hours of the application being created. The widget URL has expired.

json
{
  "event": "application.abandoned",
  "data": {
    "applicationId": "357ab3ce-55ce-4f73-82c9-dab3136c7885",
    "applicantId":   "54cbd45f-bf8e-4add-8d0c-adb5efe705c1",
    "reason":        "no_link",
    "message":       "Application abandoned — applicant did not link a bank account within 24 hours."
  },
  "timestamp": "2026-02-21T22:05:00.000Z"
}

reason: no_analyze

The applicant linked their account but /analyze was never called within 7 days. Bank data has been scrubbed from our systems for compliance.

json
{
  "event": "application.abandoned",
  "data": {
    "applicationId": "357ab3ce-55ce-4f73-82c9-dab3136c7885",
    "applicantId":   "54cbd45f-bf8e-4add-8d0c-adb5efe705c1",
    "reason":        "no_analyze",
    "message":       "Application abandoned — analysis was not submitted within 7 days of account linking. Bank data has been scrubbed."
  },
  "timestamp": "2026-02-28T22:10:00.000Z"
}
For both abandonment reasons, you must call /initiate to start a fresh application. The applicant will need to re-link their bank account.
EVENTapplication.decision

application.decision

The final scored decision. Delivered after analysis completes. See the Decision Object section for a full breakdown of all fields.

json
{
  "event": "application.decision",
  "data": {
    "applicationId": "357ab3ce-55ce-4f73-82c9-dab3136c7885",
    "applicantId":   "54cbd45f-bf8e-4add-8d0c-adb5efe705c1",
    "status":        "COMPLETED",
    "score":         720,
    "decision": {
      "score":       720,
      "decision":    "APPROVED",
      "score_band":  "LOW_RISK",
      "timestamp":   "2026-02-20T22:14:35.576410Z",
      "applicant_id": "54cbd45f-bf8e-4add-8d0c-adb5efe705c1",
      "approval_details": {
        "approved_amount":   500000,
        "approved_tenor":    12,
        "approved_interest": 5.0,
        "monthly_payment":   46667
      },
      "counter_offer": null,
      "risk_factors": [],
      "score_breakdown": {
        "total":               720,
        "cash_flow_health":    180,
        "income_stability":    160,
        "debt_service_capacity": 140,
        "account_behavior":    140,
        "credit_history":      100
      },
      "explainability": {
        "primary_reason":  "Strong income consistency and healthy cash flow",
        "key_strengths":   ["Consistent monthly credits", "Low debt-to-income ratio"],
        "key_weaknesses":  []
      },
      "eligible_tenors":  [6, 12, 18, 24],
      "manual_review_reasons": [],
      "regulatory_compliance": {
        "thin_file":             false,
        "identity_verified":     true,
        "credit_bureau_checked": true,
        "affordability_assessed": true
      }
    }
  },
  "timestamp": "2026-02-20T22:14:35.703Z"
}