oqui
  • Blog
  • Documentation
  • Pricing
  • FAQ
  • Contact
Sign InSign Up
oqui

Chaos in. Clarity out. Upload financial documents and get instant affordability insights.

© 2026 oqui. A product of Advanced Fluid Dynamics.

About
  • About
  • Blog
  • Contact
Product
  • Documentation
  • Security
Legal
  • Terms of Service
  • Privacy Policy
  • Cookie Policy
  • Introduction
    • Getting Started
    • Transaction Categories
    • Income Recognition
    • DTI Calculation
    • Verification
    • Authentication
    • Submit Assessment
    • Get Results

Get Results

Retrieve assessment results via polling or webhooks.

After submitting an assessment, retrieve results by polling or receiving a webhook.

Option 1: Polling

Poll the job status endpoint until processing completes.

Endpoint

GET /api/v1/assess/{jobId}

Polling strategy

Poll every 10-15 seconds until status is completed or failed.

async function pollForResults(jobId, apiKey) {
  const maxAttempts = 30; // 5 minutes at 10s intervals

  for (let i = 0; i < maxAttempts; i++) {
    const response = await fetch(
      `https://oqui.io/api/v1/assess/${jobId}`,
      { headers: { Authorization: `Bearer ${apiKey}` } }
    );

    const data = await response.json();

    if (data.status === 'completed') {
      return data.result;
    }

    if (data.status === 'failed') {
      throw new Error(data.error.message);
    }

    // Still processing, wait and retry
    await new Promise(r => setTimeout(r, 10000));
  }

  throw new Error('Timeout waiting for assessment');
}

Status values

StatusMeaning
pendingJob queued, not yet started
processingCurrently processing documents
completedFinished successfully, results available
failedProcessing failed, error details available

Option 2: Webhooks

Provide a webhook_url when submitting to receive results automatically.

Webhook payload

When processing completes, we POST to your webhook URL:

Success:

{
  "event": "assessment.completed",
  "job_id": "550e8400-e29b-41d4-a716-446655440000",
  "assessment_id": "660e8400-e29b-41d4-a716-446655440001",
  "status": "completed",
  "external_reference": "loan-app-12345",
  "completed_at": "2024-01-15T10:03:30Z",
  "result": { ... }
}

Failure:

{
  "event": "assessment.failed",
  "job_id": "550e8400-e29b-41d4-a716-446655440000",
  "assessment_id": "660e8400-e29b-41d4-a716-446655440001",
  "status": "failed",
  "external_reference": "loan-app-12345",
  "error": {
    "code": "BANK_STATEMENT_FAILED",
    "message": "One or more bank statements failed to process",
    "failed_documents": ["corrupted.pdf"]
  }
}

Webhook signature

Webhooks include an X-Webhook-Signature header for verification:

const crypto = require('crypto');

function verifyWebhook(payload, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

Webhook retries

If your endpoint returns a non-2xx status, we retry:

  • 3 retry attempts
  • Exponential backoff (1min, 5min, 15min)
  • After all retries fail, webhook is marked as failed

Result structure

Completed assessments include a comprehensive result object:

{
  "period": {
    "from": "2024-01-01",
    "to": "2024-03-31",
    "months_covered": 3,
    "statement_count": 3,
    "transaction_count": 245
  },
  "summary": {
    "average_monthly": {
      "recognized_income": 45000,
      "gross_income": 52000,
      "fixed_obligations": 12000,
      "essential_expenses": 8000,
      "external_debt": 5000,
      "total_obligations": 25000,
      "surplus": 20000,
      "dti_ratio": 55.5
    },
    "dti_status": "elevated",
    "income_stability": "stable"
  },
  "monthly_breakdown": [...],
  "income_analysis": {...},
  "obligations_analysis": {...},
  "verification": {...},
  "risk_indicators": {...},
  "transactions": [...],
  "affordability_rules_applied": {...}
}

Key result sections

SectionDescription
periodDate range and document counts
summaryExecutive summary with averages and DTI
monthly_breakdownMonth-by-month detail
income_analysisIncome by category with haircuts
obligations_analysisFixed and credit obligations
verificationCross-document verification results
risk_indicatorsBehavioral flags and credit issues
transactionsAll extracted transactions
affordability_rules_appliedRules used for calculation

Processing status

Both polling and webhook responses include processing_status showing per-document status:

{
  "processing_status": {
    "bank_statements": [
      { "filename": "jan-2024.pdf", "status": "completed", "transaction_count": 85 },
      { "filename": "feb-2024.pdf", "status": "completed", "transaction_count": 72 }
    ],
    "payslips": [
      { "filename": "payslip-jan.pdf", "status": "completed" }
    ],
    "credit_report": { "status": "completed" },
    "classification": { "status": "completed" },
    "verification": { "status": "completed" }
  }
}

Handling failures

When an assessment fails, the response includes error details:

{
  "status": "failed",
  "error": {
    "code": "BANK_STATEMENT_FAILED",
    "message": "Unable to extract transactions from statement",
    "failed_documents": ["corrupted.pdf"]
  }
}

Common error codes:

CodeMeaning
BANK_STATEMENT_FAILEDCould not process bank statement
CLASSIFICATION_FAILEDTransaction classification failed
PAYSLIP_FAILEDCould not process payslip
CREDIT_REPORT_FAILEDCould not process credit report
INSUFFICIENT_DATANot enough data for assessment
  1. Option 1: Polling
    1. Endpoint
    2. Polling strategy
    3. Status values
    4. Option 2: Webhooks
    5. Result structure
    6. Processing status
    7. Handling failures