Skip to main content

Event Schema Documentation

Event Schema Documentation

Project: Drop Version: 0.1.0 Date: 2026-02-23 Author: Platform Architect (AI) Status: In Review Reviewers: Alem Bašić (CEO)

Document History

Version Date Author Changes
0.1 2026-02-23 Platform Architect (AI) Initial draft — event-driven architecture not yet implemented

1. Overview

Drop's current architecture is a synchronous monolith — there is no internal event bus or message queue (no BullMQ, SQS, or RabbitMQ). All operations are handled synchronously within Next.js API route handlers.

This document covers:

  1. External webhook events received from Sumsub (KYC status updates) — the only production external event source
  2. Internal audit log events — database-level event tracking for compliance
  3. Planned event schema — for future async processing when an event bus is added

2. External Webhook Events

2.1 Sumsub — KYC Status Webhook

Source: src/lib/services/mock-sumsub.ts, production Sumsub docs: https://docs.sumsub.com/reference/applicant-review

Sumsub sends webhook events when a KYC applicant's review status changes.

Endpoint: POST /api/kyc/webhook (TBD — pending Open Banking provider integration)

Webhook signature verification: HMAC-SHA256 using SUMSUB_SECRET_KEY. Reject any webhook with invalid signature.

Event: applicantReviewed

Sent when Sumsub completes review of a KYC applicant.

{
  "type": "applicantReviewed",
  "reviewStatus": "completed",
  "applicantId": "sumsub_applicant_id",
  "externalUserId": "usr_a1b2c3d4",
  "reviewResult": {
    "reviewAnswer": "GREEN",
    "rejectLabels": [],
    "reviewRejectType": null
  },
  "createdAt": "2026-02-23T12:00:00Z"
}
Field Type Values Description
type string applicantReviewed Event type
reviewAnswer string GREEN, RED, RETRY Verification outcome
externalUserId string Drop user ID Links to Drop users.id
rejectLabels array e.g., ["DOCUMENT_UNREADABLE"] Rejection reasons (if RED/RETRY)

Drop action on receipt:

switch (reviewAnswer) {
  case 'GREEN':
    // Update users SET kyc_status = 'approved' WHERE id = externalUserId
    break;
  case 'RED':
    // Update users SET kyc_status = 'rejected' WHERE id = externalUserId
    break;
  case 'RETRY':
    // Update users SET kyc_status = 'pending' WHERE id = externalUserId
    // User must resubmit documents
    break;
}

Event: applicantPending

Sent when applicant documents are submitted and awaiting review.

{
  "type": "applicantPending",
  "applicantId": "...",
  "externalUserId": "usr_...",
  "createdAt": "2026-02-23T12:00:00Z"
}

Drop action: Update users SET kyc_status = 'pending'.


2.2 Open Banking Provider — Payment Webhooks (Planned)

When an Open Banking provider is selected, the following webhook events will be received for PISP payment status updates.

Endpoint: POST /api/payments/webhook (future)

Event: payment.settled

{
  "event": "payment.settled",
  "paymentId": "provider_payment_id",
  "dropTransactionId": "tx_rem_abc123",
  "status": "completed",
  "settledAt": "2026-02-23T14:30:00Z",
  "amount": 2000,
  "currency": "NOK"
}

Drop action: UPDATE transactions SET status = 'completed', completed_at = NOW() WHERE id = dropTransactionId

Event: payment.failed

{
  "event": "payment.failed",
  "paymentId": "provider_payment_id",
  "dropTransactionId": "tx_rem_abc123",
  "status": "failed",
  "failureReason": "InsufficientFunds",
  "failedAt": "2026-02-23T14:30:00Z"
}

Drop action: UPDATE transactions SET status = 'failed' WHERE id = dropTransactionId, refund bank account balance.


2.3 BankID — No Webhook Events

BankID OIDC is a synchronous request-response flow. No webhooks are received from BankID. The callback URL (GET /api/auth/bankid/callback) handles the code exchange synchronously.


3. Internal Audit Log Events

Drop maintains an audit_log table for all significant application events. This functions as an internal event log for compliance (AML, GDPR) and security investigations.

3.1 Audit Log Schema

CREATE TABLE audit_log (
  id TEXT PRIMARY KEY,
  action TEXT NOT NULL,          -- Event type (see below)
  user_id TEXT,                  -- Acting user (null for system events)
  resource_type TEXT,            -- Entity type (user, transaction, secret, etc.)
  resource_id TEXT,              -- Entity ID
  details TEXT,                  -- JSON blob with event-specific data
  ip_address TEXT,               -- Client IP at time of action
  timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
);

3.2 Audit Event Types

Action Trigger Resource Type Details
user_registered New user created via BankID user { kyc_method, auth_provider }
kyc_approved KYC status → approved user { sumsub_applicant_id, review_answer }
kyc_rejected KYC status → rejected user { reject_labels, review_answer }
transaction_created Remittance or QR payment initiated transaction { type, amount, fee, currency }
transaction_completed Payment confirmed by provider transaction { settled_at, provider_ref }
transaction_failed Payment rejected transaction { failure_reason }
session_created User logged in session { auth_method }
sessions_revoked User logged out session { revoked_count }
account_deleted GDPR erasure request user { deleted_at, retention_note }
data_exported GDPR data export user { export_timestamp }
consent_granted GDPR consent given consent { consent_type, ip_address }
consent_withdrawn GDPR consent revoked consent { consent_type, ip_address }
complaint_submitted User complaint filed complaint { category, subject }
aml_alert_created AML monitoring flagged activity aml_alert { alert_type, severity, transaction_id }
str_filed STR filed with Finanstilsynet str_report { filed_at, case_number }
secret_rotated Secret key rotated secret { provider, key_name, rotated_at }
merchant_registered New merchant account merchant { org_number, business_name }

3.3 Example Audit Log Entry

{
  "id": "aud_1a2b3c4d5e6f7890",
  "action": "transaction_created",
  "user_id": "usr_abc123",
  "resource_type": "transaction",
  "resource_id": "tx_rem_xyz789",
  "details": {
    "type": "remittance",
    "amount": 2000,
    "fee": 10,
    "currency": "NOK",
    "recipient_country": "RS",
    "exchange_rate": 11.7
  },
  "ip_address": "85.20.12.45",
  "timestamp": "2026-02-23T14:30:00.000Z"
}

3.4 Querying Audit Logs

-- Recent user activity
SELECT * FROM audit_log
WHERE user_id = 'usr_abc123'
ORDER BY timestamp DESC
LIMIT 50;

-- Security: all secret rotations
SELECT * FROM audit_log
WHERE action = 'secret_rotated'
ORDER BY timestamp DESC;

-- AML: suspicious transactions
SELECT al.* FROM audit_log al
JOIN transactions t ON al.resource_id = t.id
WHERE al.action = 'transaction_created'
  AND t.send_amount > 50000  -- High-value transactions
  AND al.timestamp > NOW() - INTERVAL '24 hours'
ORDER BY al.timestamp DESC;

Retention: Audit logs retained for 5 years per hvitvaskingsloven (Norwegian AML law).


4. Planned Event-Driven Architecture (Future)

When Drop scales beyond MVP, an event bus will decouple synchronous operations:

4.1 Proposed Event Bus

Technology options:

  • AWS SQS + SNS (native AWS, fits existing infrastructure)
  • BullMQ + Redis (simpler, Node.js native)

Recommendation: SQS for production reliability.

4.2 Planned Event Topics

Topic Publisher Subscribers Purpose
drop.transaction.initiated Payment service PISP provider, Audit, Notification Trigger payment + notify user
drop.transaction.settled Open Banking webhook handler Audit, Notification, AML Update transaction status
drop.kyc.status_changed Sumsub webhook handler Audit, User service Update user KYC status
drop.user.registered Auth service KYC, Notification Trigger KYC flow
drop.aml.alert AML monitoring Compliance, Notification Flag suspicious activity

4.3 Planned Event Envelope

{
  "eventId": "evt_unique_id",
  "eventType": "drop.transaction.initiated",
  "version": "1.0",
  "timestamp": "2026-02-23T14:30:00Z",
  "source": "drop-payment-service",
  "correlationId": "req_abc123",
  "data": {
    "transactionId": "tx_rem_xyz",
    "userId": "usr_abc",
    "amount": 2000,
    "currency": "NOK"
  }
}

5. Slack Alert Events (Operational)

Slack operational alerts from src/lib/alerts.ts function as a simple event notification system:

Severity Event Trigger
info App startup Application boots
info App shutdown Graceful shutdown
critical Error spike > 5 errors in 60 seconds
critical Unhandled exception Process error handler

These are fire-and-forget HTTP POST calls — no acknowledgement or retry logic.



Approval

Role Name Date Signature
Author Platform Architect (AI) 2026-02-23
Reviewer
Approver Alem Bašić