Skip to content

Withdrawal

Withdrawal is one platform-wide endpoint shared by every product: POST /v1/withdrawals with reference_type + reference_id + amount. Internally, the endpoint routes to per-product handlers that debit the matching holding and credit the main balance. An optional follow-on disbursement sends the funds to an external bank account via the payment gateway.

A withdrawal is two movements stitched together. The first is internal: the per-product holding (saving goal, investment, saving circle) is debited and the owner’s main balance is credited — this is usually synchronous and can auto-approve. The second is optional and external: a disbursement debits the main balance and sends the funds to a bank account through the payment gateway, with an async signed callback that mirrors top-up in reverse.

flowchart LR
    U([Account owner])
    REQ[/"POST /v1/withdrawals<br/>reference_type · reference_id<br/>amount"/]

    subgraph SOURCES[" Source holdings "]
        direction TB
        SG_H[("Saving Goal<br/>holding")]
        INV_H[("Investment<br/>holding")]
        SC_H[("Saving Circle<br/>holding")]
    end

    MAIN[("Main balance")]
    GW[("Payment gateway<br/>disbursement")]
    BANK[/"Bank account<br/>end of the line"/]

    U -->|initiate| REQ
    REQ -->|debit| SG_H
    REQ -->|debit| INV_H
    REQ -->|debit| SC_H
    SG_H -->|credit| MAIN
    INV_H -->|credit| MAIN
    SC_H -->|credit| MAIN
    MAIN -->|optional disbursement| GW
    GW -->|transfer| BANK

    classDef money fill:#FDF8E8,stroke:#3C2C96,stroke-width:2px,color:#1E1B4B;
    classDef edge fill:#FFF3D0,stroke:#F4BE27,stroke-width:1.5px,color:#8B5A00;
    classDef user fill:#E8E1FF,stroke:#3C2C96,stroke-width:1.5px,color:#1E1B4B;
    class MAIN,SG_H,INV_H,SC_H,GW money;
    class REQ,BANK edge;
    class U user;

Two-step model. Step 1 moves funds from the product-specific holding back to the main balance — this part is usually synchronous and can auto-approve. Step 2, when requested, is the disbursement from the main balance to an external bank via the payment gateway — an async flow with a signed callback that mirrors top-up in reverse.

---
config:
  sequence:
    actorMargin: 320
    width: 220
    messageMargin: 38
    boxMargin: 14
    noteMargin: 12
---
sequenceDiagram
    autonumber
    actor U as Account owner
    participant API as Moria API
    actor AD as Admin reviewer
    participant GW as Payment Gateway

    Note over U,GW: Two-step model<br/>• step 1: holding → main balance (internal)<br/>• step 2: main balance → bank (disbursement via gateway)

    U->>API: POST /v1/withdrawals<br/>{ reference_type, reference_id,<br/>amount, destination? }
    API->>API: validate ownership · check holding balance<br/>set status: "pending"<br/>route to handler by reference_type
    API-->>U: 201 Created<br/>{ id, status: "pending" }

    Note over AD,API: Admin review · applies to disbursements that need approval<br/>internal-only moves can auto-approve
    AD->>API: approve the withdrawal request
    API->>API: debit source holding<br/>credit main balance · status → "approved"

    opt disbursement to external bank
        API->>GW: create disbursement transaction<br/>(account_number, amount)
        GW-->>API: { trx_id, status: processing }
        GW->>API: callback · status: success
        API->>API: debit main balance · mark disbursed
    end

    U->>API: GET /v1/withdrawals/:id
    API-->>U: 200 OK<br/>{ status: "approved", history[] }