marketplace-doc
    • Data Ingestion
    • Errors
    • Introduction
    • Loan API & Deduction Lifecycle
    • Getting Started
    • OAuth
    • Webhooks
    • Embedded Journey
    • OpenSylo Marketplace Integration API
      • OAuth 2.0
        • Start OAuth authorization
        • Exchange authorization code or refresh token
        • Revoke a token
        • Discover OAuth capabilities
      • Data Ingestion
        • Submit single merchant data
        • Submit bulk merchant data
        • Poll batch processing status
        • Get merchant credit score
        • Data ingestion health check
      • Sales & Events
        • Submit a sales event
        • Submit a repayment event
        • Submit an account flag
      • Loan API
        • Get active loans for a merchant
        • Get loan status
        • Validate deduction amounts
        • Bulk loan status
      • Inbound Webhooks
        • Repayment events
        • Settlement events
      • Outbound Webhooks
        • loan.approved
        • loan.disbursed
        • loan.repayment_updated
        • loan.nearly_complete
        • loan.completed
        • loan.defaulted
        • merchant.created
        • kyc.submitted
        • kyc.approved
        • kyc.rejected
        • funding_request.created
        • funding_request.fulfilled
        • funding_request.rejected
      • Embedded Journey (Marketplace API)
        • Create (or fetch) a merchant
        • Get merchant status (KYC, credit score, funding requests)
        • Update business KYC information
        • Add directors (bulk)
        • Attach a KYC document
        • Submit KYC for review
        • Submit sales data for credit scoring
        • Create a funding request
        • Mint an embed token for the hosted journey
      • Schemas
        • TokenRequest
        • TokenResponse
        • OAuthError
        • ClientMetadataResponse
        • MerchantIdentity
        • SalesPerformance
        • RevenueConsistency
        • FulfillmentMetrics
        • PayoutCashFlow
        • PlatformDependency
        • HistoricalCredit
        • BehavioralRisk
        • MonthlyHistoryEntry
        • MerchantDataRequest
        • CreditScore
        • LoanEligibilityResult
        • MerchantDataResponse
        • BulkMerchantDataRequest
        • AsyncBulkProcessingResponse
        • BatchStatusResponse
        • SalesEventRequest
        • RepaymentEventRequest
        • AccountFlagRequest
        • MerchantInfo
        • LenderInfo
        • RepaymentInfo
        • LoanTermsInfo
        • ActiveLoan
        • ActiveLoansSummary
        • ActiveLoansResponse
        • LoanStatusRepayment
        • LoanStatusResponse
        • ValidateDeductionsRequest
        • DeductionItem
        • DeductionSummary
        • ValidateDeductionsResponse
        • BulkLoanStatusRequest
        • BulkLoanStatusItem
        • BulkStatusSummary
        • BulkLoanStatusResponse
        • WebhookMarketplace
        • WebhookMerchant
        • WebhookLoan
        • DeductionDetails
        • SourceTransaction
        • LoanBalance
        • RepaymentDeductedWebhook
        • FailureDetails
        • RepaymentFailedWebhook
        • ReversalDetails
        • RepaymentReversedWebhook
        • SettlementDetails
        • SettlementSummaryByStatus
        • SettlementSummary
        • SettlementLoanIncluded
        • BankTransferDetails
        • SettlementCreatedWebhook
        • SettlementTransferredWebhook
        • TransferDetails
        • WebhookAckResponse
        • OutboundLoan
        • OutboundMerchant
        • OutboundLender
        • LoanTerms
        • RepaymentTerms
        • ScheduleInstallment
        • LoanApprovedPayload
        • DisbursementDetails
        • DisbursedRepayment
        • LoanDisbursedPayload
        • ChangeDetail
        • LoanRepaymentUpdatedPayload
        • RepaymentStatus
        • NearlyCompleteRecommendation
        • LoanNearlyCompletePayload
        • CompletionSummary
        • LoanCompletedPayload
        • DefaultDetails
        • CollectionInstructions
        • LoanDefaultedPayload
        • ApiError
        • RateLimitError
        • EmbedCreateMerchantRequest
        • EmbedCreateMerchantResponse
        • EmbedBusinessKyc
        • EmbedDirectorInfo
        • EmbedBulkDirectors
        • EmbedDirectorListItem
        • EmbedDocumentUpload
        • EmbedSalesDataAccepted
        • EmbedCreateFundingRequest
        • EmbedFundingRequestIntent
        • EmbedMerchantStatusResponse
        • EmbedTokenResponse
        • MarketplaceMerchantRef
        • MerchantCreatedPayload
        • KycStatusPayload
        • FundingRequestEventPayload
    • OpenSylo Marketplace API
      • OAuth 2.0
        • Start OAuth authorization
        • Exchange code or refresh token
        • Revoke a token
        • OAuth discovery / client metadata
      • Data Ingestion
        • Submit single merchant data
        • Submit bulk merchant data
        • Get merchant credit score
        • Integration health check
      • Loan API
        • Get active loans for a merchant
        • Get loan status
        • Validate deduction amounts
        • Bulk loan status check
      • Inbound Webhooks
        • Send repayment webhook
        • Send settlement webhook
      • Schemas
        • TokenRequest
        • TokenResponse
        • RevokeRequest
        • ClientMetadataResponse
        • MerchantIdentity
        • SalesPerformance
        • RevenueConsistency
        • FulfillmentMetrics
        • PayoutCashFlow
        • PlatformDependency
        • HistoricalCredit
        • BehavioralRisk
        • MerchantDataRequest
        • ScoreBreakdown
        • CreditScore
        • MerchantDataResponse
        • BulkMerchantDataRequest
        • BulkMerchantDataResponse
        • CreditScoreResponse
        • HealthResponse
        • ActiveLoansResponse
        • LoanStatusResponse
        • ValidateDeductionsRequest
        • ValidateDeductionsResponse
        • BulkLoanStatusRequest
        • BulkLoanStatusResponse
        • RepaymentWebhookRequest
        • SettlementWebhookRequest
        • WebhookAckResponse
        • OAuthError
        • ApiError

    Loan API & Deduction Lifecycle

    Overview#

    Marketplaces manage loan repayments by deducting a percentage of each merchant sale and reporting those deductions to OpenSylo. This guide walks through the full deduction lifecycle, from loan approval to completion.

    The Deduction Lifecycle#

    1. loan.approved       Store loan terms and deduction settings
           |
    2. loan.disbursed      Start deducting from merchant sales
           |
    3. On each sale        Validate -> Deduct -> Report
           |
    4. loan.nearly_complete  Cap deductions using max_deduction_amount
           |
    5. loan.completed      Stop all deductions immediately
           |
    6. settlement.created  Batch deductions for a period
           |
    7. settlement.transferred  Confirm bank transfer
    Step by step:
    1.
    Receive loan.approved outbound webhook -- Store the loan terms, deduction percentage, minimum deduction amount, and repayment schedule. No deductions yet.
    2.
    Receive loan.disbursed outbound webhook -- The merchant has received funds. Start deducting the specified percentage from every sale.
    3.
    On each merchant sale:
    Call POST /api/v1/marketplace/loans/deductions/validate with the sale_amount
    The response tells you: should_deduct, per-loan deduction amounts, and merchant_receives
    Apply deductions to the merchant's settlement
    Send a repayment.deducted inbound webhook to OpenSylo
    4.
    Receive loan.nearly_complete -- The loan is >90% repaid. Cap the next deduction using the max_deduction_amount from the webhook to avoid over-collection.
    5.
    Receive loan.completed -- Stop all deductions for this loan immediately. Any excess collected will be reconciled.
    6.
    Periodically send settlement.created -- Batch all deductions for a period (daily or weekly) into a settlement and notify OpenSylo.
    7.
    After bank transfer, send settlement.transferred -- Confirm that the settlement funds have been transferred to OpenSylo's bank account.

    Querying Active Loans#

    Endpoint: GET /api/v1/marketplace/loans/active?merchant_id=xxx
    Returns all active loans for a merchant, including deduction settings and outstanding balances. Use this on startup to initialize your deduction state.
    Response:
    {
      "merchant_id": "merch_001",
      "active_loans": [
        {
          "loan_id": "loan_uuid_1",
          "reference": "LOAN-2026-001",
          "principal_amount": 500000,
          "total_repayment": 525000,
          "outstanding_balance": 175000,
          "deduction_percentage": 10,
          "minimum_deduction": 500,
          "currency": "NGN",
          "status": "ACTIVE",
          "disbursed_at": "2026-02-01T09:00:00.000Z",
          "due_date": "2026-05-01T00:00:00.000Z"
        }
      ],
      "total_outstanding": 175000
    }

    Checking Loan Status#

    Endpoint: GET /api/v1/marketplace/loans/{loanId}/status
    Returns the current status of a specific loan, including whether deductions are required.
    Response:
    {
      "loan_id": "loan_uuid_1",
      "reference": "LOAN-2026-001",
      "status": "ACTIVE",
      "deduction_required": true,
      "deduction_percentage": 10,
      "collection_priority": "NORMAL",
      "is_overdue": false,
      "total_due": 525000,
      "total_paid": 350000,
      "outstanding": 175000,
      "percent_complete": 66.67,
      "due_date": "2026-05-01T00:00:00.000Z"
    }

    Validating Deductions#

    Endpoint: POST /api/v1/marketplace/loans/deductions/validate
    Call this before every deduction to get the exact amounts to deduct per loan. The response accounts for multiple active loans, capped amounts for nearly-complete loans, and minimum deduction thresholds.
    Response:
    {
      "should_deduct": true,
      "sale_amount": 50000,
      "total_deduction": 5000,
      "merchant_receives": 45000,
      "currency": "NGN",
      "loans": [
        {
          "loan_id": "loan_uuid_1",
          "reference": "LOAN-2026-001",
          "deduction_amount": 5000,
          "deduction_percentage": 10,
          "outstanding_after": 170000,
          "is_capped": false
        }
      ],
      "validation_id": "val_uuid",
      "valid_until": "2026-01-29T10:45:00.000Z"
    }
    Key points:
    The validation_id is valid for 15 minutes. Apply the deduction within that window.
    When should_deduct is false, the sale amount is below all minimum deduction thresholds -- pass the full amount to the merchant.
    When is_capped is true for a loan, the deduction amount has been capped to avoid over-collection (loan is nearly complete).
    For merchants with multiple active loans, deductions are split proportionally across loans.

    Multiple Active Loans Example#

    {
      "should_deduct": true,
      "sale_amount": 50000,
      "total_deduction": 7500,
      "merchant_receives": 42500,
      "currency": "NGN",
      "loans": [
        {
          "loan_id": "loan_uuid_1",
          "reference": "LOAN-2026-001",
          "deduction_amount": 5000,
          "deduction_percentage": 10,
          "outstanding_after": 170000,
          "is_capped": false
        },
        {
          "loan_id": "loan_uuid_2",
          "reference": "LOAN-2026-002",
          "deduction_amount": 2500,
          "deduction_percentage": 5,
          "outstanding_after": 97500,
          "is_capped": false
        }
      ],
      "validation_id": "val_uuid",
      "valid_until": "2026-01-29T10:45:00.000Z"
    }

    Bulk Status Check#

    Endpoint: POST /api/v1/marketplace/loans/bulk-status
    Check the status of multiple loans in a single request. Useful for periodic reconciliation.
    Response:
    {
      "loans": [
        {
          "loan_id": "loan_uuid_1",
          "reference": "LOAN-2026-001",
          "status": "ACTIVE",
          "deduction_required": true,
          "outstanding": 175000
        },
        {
          "loan_id": "loan_uuid_2",
          "reference": "LOAN-2026-002",
          "status": "REPAID",
          "deduction_required": false,
          "outstanding": 0
        },
        {
          "loan_id": "loan_uuid_3",
          "reference": "LOAN-2026-003",
          "status": "ACTIVE",
          "deduction_required": true,
          "outstanding": 300000
        }
      ],
      "checked_at": "2026-01-29T10:00:00.000Z"
    }

    Settlement Flow#

    Settlements batch multiple deductions over a period and facilitate the transfer of collected funds to OpenSylo.

    1. Accumulate Deductions#

    As you deduct from merchant sales, keep a running total per settlement period (daily or weekly). Each deduction should be reported immediately via the repayment.deducted inbound webhook.

    2. Send settlement.created#

    At the end of each settlement period, send a settlement.created webhook to OpenSylo with the full summary:
    {
      "event": "settlement.created",
      "event_id": "evt_stl_001",
      "timestamp": "2026-02-03T00:00:00.000Z",
      "marketplace": {
        "id": "marketplace_uuid",
        "name": "Your Marketplace"
      },
      "settlement": {
        "reference": "stl_2026_w05",
        "period_start": "2026-01-27T00:00:00.000Z",
        "period_end": "2026-02-02T23:59:59.000Z",
        "currency": "NGN",
        "created_at": "2026-02-03T00:00:00.000Z"
      },
      "summary": {
        "total_deductions": 12,
        "total_amount": 60000,
        "merchants_included": 3
      },
      "loans_included": [
        {
          "opensylo_id": "loan_uuid_1",
          "opensylo_reference": "LOAN-2026-001",
          "deduction_count": 8,
          "deduction_total": 40000
        },
        {
          "opensylo_id": "loan_uuid_2",
          "opensylo_reference": "LOAN-2026-002",
          "deduction_count": 4,
          "deduction_total": 20000
        }
      ],
      "bank_transfer": {
        "status": "pending",
        "destination_bank": "OpenSylo Collections",
        "destination_account": "0123456789"
      }
    }

    3. Transfer Funds#

    Transfer the total settlement amount to OpenSylo's designated bank account.

    4. Send settlement.transferred#

    After the bank transfer is confirmed, send a settlement.transferred webhook:
    {
      "event": "settlement.transferred",
      "event_id": "evt_stl_002",
      "timestamp": "2026-02-03T10:30:00.000Z",
      "marketplace": {
        "id": "marketplace_uuid",
        "name": "Your Marketplace"
      },
      "settlement": {
        "reference": "stl_2026_w05",
        "currency": "NGN"
      },
      "transfer": {
        "reference": "txn_bank_001",
        "amount": 60000,
        "bank_name": "OpenSylo Collections",
        "account_number": "0123456789",
        "transferred_at": "2026-02-03T10:30:00.000Z",
        "bank_reference": "NIP/230203/ABCDEF"
      }
    }

    Handling Edge Cases#

    Loan Completed Mid-Sale#

    If you receive a loan.completed webhook while processing a sale, stop deducting for that loan immediately. If a deduction has already been applied but not yet reported, send a repayment.reversed webhook for the excess amount.

    Multiple Active Loans#

    When a merchant has multiple active loans, the deduction validation endpoint splits the deduction proportionally. Always use the validate endpoint rather than calculating splits yourself -- the proportions may change as loans approach completion.

    Failed Deduction#

    If a deduction cannot be applied (e.g., insufficient merchant balance, system error), send a repayment.failed webhook:
    {
      "event": "repayment.failed",
      "event_id": "evt_rep_fail_001",
      "failure": {
        "reference": "fail_ref_001",
        "attempted_amount": 5000,
        "reason": "Merchant wallet balance insufficient for deduction",
        "code": "INSUFFICIENT_BALANCE",
        "attempted_at": "2026-01-30T11:00:00.000Z"
      }
    }

    Reversed Deduction#

    If a sale is refunded after a deduction was applied, send a repayment.reversed webhook:
    {
      "event": "repayment.reversed",
      "event_id": "evt_rep_rev_001",
      "reversal": {
        "reference": "rev_ref_001",
        "original_deduction_reference": "deduct_ref_001",
        "amount": 5000,
        "reason": "Original sale was refunded by buyer",
        "reversed_at": "2026-02-01T14:00:00.000Z"
      }
    }

    Defaulted Loan#

    When you receive a loan.defaulted webhook, follow the collection_instructions in the payload. This typically means increasing the deduction percentage and optionally withholding merchant settlements until the outstanding balance is recovered.

    Reconciliation#

    Run periodic reconciliation by calling the bulk status endpoint and comparing your local records with OpenSylo's. Any discrepancies should be resolved by sending corrective webhooks (reversals for over-deductions, additional deduction reports for under-reporting).
    Modified at 2026-04-08 18:18:53
    Previous
    Introduction
    Next
    Getting Started
    Built with