> ## Documentation Index
> Fetch the complete documentation index at: https://docs.monigo.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Marketplace Platforms

> Bill sellers, collect commissions, and pay out earnings for an auction or listing marketplace like eBay.

Marketplaces are harder to bill than SaaS. You have two-sided fees (listing charges and final-value commissions), multi-recipient payouts, premium seller tiers, and refunds that have to ripple back through both the seller's fees and the buyer's payment. This guide shows how to compose Monigo's primitives — metrics, plans, subscriptions, and events — into a working end-to-end marketplace billing layer.

<Info>
  The patterns here apply to most marketplaces — auction-style platforms like eBay, goods marketplaces like Etsy and Jumia, and services marketplaces. The worked example uses an eBay-style auction flow.
</Info>

## Mapping marketplace concepts to Monigo

| Marketplace concept      | Monigo primitive                                                              |
| ------------------------ | ----------------------------------------------------------------------------- |
| Sellers                  | Customers, tagged with `metadata.role = "seller"`                             |
| Listing fee per item     | Metric with `count` aggregation, priced on a **collection** plan              |
| Final-value commission   | Metric with `sum` aggregation over a property you populate on each sale event |
| Seller earnings (payout) | Metric with `sum` aggregation, priced on a **payout** plan                    |
| Premium seller tier      | A second collection plan with different pricing                               |
| Seller bank account      | Payout account on the seller customer                                         |

Every seller carries **two** Monigo subscriptions: a **collection** plan that invoices them for listing fees and platform commission, and a **payout** plan that generates bills for their earnings. Monigo enforces this one-of-each rule at the API level — a customer can have exactly one active collection subscription and one active payout subscription at a time.

***

## End-to-end flow

```mermaid theme={null}
sequenceDiagram
    participant S as Seller
    participant P as Your Marketplace
    participant M as Monigo
    participant B as Buyer
    participant PSP as Payment Provider

    S->>P: List an item
    P->>M: POST /v1/ingest { item_listed }

    B->>P: Buy the item
    P->>PSP: Charge buyer
    P->>M: POST /v1/ingest { item_sold, properties }
    Note over M: Rolls up commission on collection plan<br/>Rolls up seller share on payout plan

    Note over M: Period end (cron)
    M-->>P: invoice.finalized (fees owed by seller)
    M-->>P: Bill generated (earnings owed to seller)

    P->>M: Initiate payout from dashboard
    M->>PSP: Transfer seller's share
    PSP-->>M: Transfer confirmed
    M-->>P: payout.success webhook
```

***

## 1. Create sellers as customers

Every seller is a Monigo customer. Tag them with `metadata.role` so your code can distinguish sellers from buyers downstream.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.monigo.co/v1/customers \
    -H "Authorization: Bearer mk_live_..." \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Ada Power-Seller",
      "email": "ada@example.com",
      "metadata": { "role": "seller" }
    }'
  ```

  ```go Go theme={null}
  seller, err := client.Customers.Create(ctx, monigo.CreateCustomerRequest{
      Name:     "Ada Power-Seller",
      Email:    "ada@example.com",
      Metadata: map[string]any{"role": "seller"},
  })
  ```

  ```ts TypeScript theme={null}
  const seller = await client.customers.create({
    name: 'Ada Power-Seller',
    email: 'ada@example.com',
    metadata: { role: 'seller' },
  })
  ```
</CodeGroup>

***

## 2. Register the seller's payout account

Sellers receive their earnings into a bank account linked to the customer record. Register it before the first payout is due.

```bash theme={null}
curl -X POST https://api.monigo.co/v1/customers/<seller_id>/payout-accounts \
  -H "Authorization: Bearer mk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "payout_method": "bank_transfer",
    "account_name": "Ada Power-Seller",
    "bank_name": "Access Bank",
    "bank_code": "044",
    "account_number": "0123456789",
    "currency": "NGN",
    "is_default": true
  }'
```

The account is looked up by `currency` when a payout is initiated, so set `is_default: true` on the seller's primary account in each currency you pay out.

***

## 3. Define the usage metrics

Monigo bills from **metrics** — named aggregations over the events you ingest. A marketplace needs three:

| Metric                | Event         | Aggregation                           | Purpose                                         |
| --------------------- | ------------- | ------------------------------------- | ----------------------------------------------- |
| `listings`            | `item_listed` | `count`                               | One unit per listing — drives the listing fee   |
| `platform_commission` | `item_sold`   | `sum` of `commission_amount` property | Total commission owed by the seller that period |
| `seller_earnings`     | `item_sold`   | `sum` of `seller_share` property      | Total amount the seller is owed that period     |

`sum`-aggregation metrics require an `aggregation_property` naming the event property that carries the numeric value. You compute the commission (10%) and seller share (90%) in your own code when the sale completes, and pass both as properties on the `item_sold` event.

```bash theme={null}
curl -X POST https://api.monigo.co/v1/metrics \
  -H "Authorization: Bearer mk_live_..." \
  -d '{
    "name": "Listings",
    "event_name": "item_listed",
    "aggregation": "count"
  }'

curl -X POST https://api.monigo.co/v1/metrics \
  -H "Authorization: Bearer mk_live_..." \
  -d '{
    "name": "Platform Commission",
    "event_name": "item_sold",
    "aggregation": "sum",
    "aggregation_property": "commission_amount"
  }'

curl -X POST https://api.monigo.co/v1/metrics \
  -H "Authorization: Bearer mk_live_..." \
  -d '{
    "name": "Seller Earnings",
    "event_name": "item_sold",
    "aggregation": "sum",
    "aggregation_property": "seller_share"
  }'
```

***

## 4. Create the fee plan and the earnings plan

A seller is billed by a **collection** plan (the fees you charge them) and paid by a **payout** plan (the earnings you owe them). Both plans point at the metrics you just defined.

**Collection plan — listing fee + commission:**

```bash theme={null}
curl -X POST https://api.monigo.co/v1/plans \
  -H "Authorization: Bearer mk_live_..." \
  -d '{
    "name": "Seller Fees — Standard",
    "currency": "NGN",
    "plan_type": "collection",
    "billing_period": "monthly",
    "prices": [
      {
        "metric_id": "<listings_metric_id>",
        "model": "flat_unit",
        "unit_price": "100.000000"
      },
      {
        "metric_id": "<platform_commission_metric_id>",
        "model": "flat_unit",
        "unit_price": "1.000000"
      }
    ]
  }'
```

₦100 per listing, then ₦1 per unit of `platform_commission`. Because `platform_commission` sums the `commission_amount` property and you pass the commission in naira, one unit = ₦1 and the commission is passed through literally.

**Payout plan — seller earnings:**

```bash theme={null}
curl -X POST https://api.monigo.co/v1/plans \
  -H "Authorization: Bearer mk_live_..." \
  -d '{
    "name": "Seller Earnings",
    "currency": "NGN",
    "plan_type": "payout",
    "billing_period": "monthly",
    "prices": [
      {
        "metric_id": "<seller_earnings_metric_id>",
        "model": "flat_unit",
        "unit_price": "1.000000"
      }
    ]
  }'
```

***

## 5. Subscribe the seller to both plans

A customer can hold at most one active collection subscription and one active payout subscription at a time. Create both:

```bash theme={null}
# Charge the seller for listings and commission
curl -X POST https://api.monigo.co/v1/subscriptions \
  -H "Authorization: Bearer mk_live_..." \
  -d '{ "customer_id": "<seller_id>", "plan_id": "<seller_fees_plan_id>" }'

# Track what you owe the seller
curl -X POST https://api.monigo.co/v1/subscriptions \
  -H "Authorization: Bearer mk_live_..." \
  -d '{ "customer_id": "<seller_id>", "plan_id": "<seller_earnings_plan_id>" }'
```

If the seller already has an active subscription of the same plan type, the API returns `409 Conflict`.

***

## 6. Ingest a listing event

Events are sent in batches to `POST /v1/ingest`. For each listing, send one event against `item_listed`:

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.monigo.co/v1/ingest \
    -H "Authorization: Bearer mk_live_..." \
    -H "Content-Type: application/json" \
    -d '{
      "events": [
        {
          "event_name": "item_listed",
          "customer_id": "<seller_id>",
          "idempotency_key": "listing_<listing_id>",
          "timestamp": "2026-04-16T10:00:00Z",
          "properties": {
            "listing_id": "<listing_id>",
            "category": "electronics"
          }
        }
      ]
    }'
  ```

  ```go Go theme={null}
  _, err := client.Events.Ingest(ctx, monigo.IngestRequest{
      Events: []monigo.IngestEvent{
          {
              EventName:      "item_listed",
              CustomerID:     sellerID,
              IdempotencyKey: "listing_" + listingID,
              Timestamp:      time.Now().UTC(),
              Properties: map[string]any{
                  "listing_id": listingID,
                  "category":   "electronics",
              },
          },
      },
  })
  ```

  ```ts TypeScript theme={null}
  await client.events.ingest({
    events: [
      {
        event_name: 'item_listed',
        customer_id: sellerId,
        idempotency_key: `listing_${listingId}`,
        timestamp: new Date().toISOString(),
        properties: {
          listing_id: listingId,
          category: 'electronics',
        },
      },
    ],
  })
  ```
</CodeGroup>

The `idempotency_key` must be unique per listing — resending the same key is a safe no-op. See [Idempotency](/guides/idempotency).

<Note>
  The ingest API requires an API key with the `ingest` scope. Create a dedicated key in **Dashboard → API Keys** and use it from your event-emitting services.
</Note>

***

## 7. Ingest a sale event

When a buyer completes a purchase, compute the commission and seller share on your side, then ingest a single `item_sold` event carrying both values in its properties. One event drives two metrics:

```bash theme={null}
curl -X POST https://api.monigo.co/v1/ingest \
  -H "Authorization: Bearer mk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "events": [
      {
        "event_name": "item_sold",
        "customer_id": "<seller_id>",
        "idempotency_key": "sale_<sale_id>",
        "timestamp": "2026-04-16T14:30:00Z",
        "properties": {
          "listing_id": "<listing_id>",
          "buyer_id": "<buyer_id>",
          "final_price": 25000,
          "commission_amount": 2500,
          "seller_share": 22500
        }
      }
    ]
  }'
```

For a ₦25 000 sale with a 10% commission rate:

* `platform_commission` rolls up by 2 500, adding ₦2 500 to the seller's fees invoice.
* `seller_earnings` rolls up by 22 500, adding ₦22 500 to the seller's payout bill.

Because you compute the commission split in your own code, you can change commission tiers, run promotions, or apply category-specific rates without modifying any Monigo configuration — just change the numbers you put in the event.

***

## 8. Period close and payout

At the end of each billing period, Monigo runs two jobs automatically:

1. **Invoice generation** for the collection plan — a draft invoice is created with line items for listings and commission, then finalized and sent for payment on the seller's payment method.
2. **Bill generation** for the payout plan — a draft bill is created with a `seller_earnings` line item, then finalized.

Initiating the actual transfer of funds to the seller is a **deliberate manual step** today. In **Dashboard → Bills**, select finalized bills and click **Initiate Payout**. Monigo looks up the seller's default payout account for the bill's currency, submits the transfer via your connected payment processor, and updates the bill as the transfer settles.

<Warning>
  There is no public API endpoint to initiate a payout programmatically at this time. Marketplaces that need fully automated payouts should subscribe to the `invoice.finalized` webhook for bills and process them in a scheduled dashboard session, or reach out to us about API access for bill actions.
</Warning>

Once the transfer is submitted, Monigo emits `payout.success` or `payout.failed` as the payment provider confirms settlement.

***

## Premium seller tiers

Power sellers often get reduced commission in exchange for higher volume commitments. Model this as a separate collection plan — free listings and a lower commission multiplier — then move the seller onto it when they qualify.

```bash theme={null}
curl -X POST https://api.monigo.co/v1/plans \
  -H "Authorization: Bearer mk_live_..." \
  -d '{
    "name": "Seller Fees — Power Seller",
    "currency": "NGN",
    "plan_type": "collection",
    "billing_period": "monthly",
    "prices": [
      {
        "metric_id": "<listings_metric_id>",
        "model": "flat_unit",
        "unit_price": "0.000000"
      },
      {
        "metric_id": "<platform_commission_metric_id>",
        "model": "flat_unit",
        "unit_price": "1.000000"
      }
    ]
  }'
```

The commission metric unit price stays at ₦1 — your application code now computes 6% of `final_price` instead of 10% when populating `commission_amount`. This way, changing commission rates never requires touching Monigo plans.

To move an existing seller onto the Power Seller plan, cancel their current collection subscription and create a new one. Their payout subscription is unaffected.

```bash theme={null}
curl -X DELETE https://api.monigo.co/v1/subscriptions/<collection_subscription_id> \
  -H "Authorization: Bearer mk_live_..."

curl -X POST https://api.monigo.co/v1/subscriptions \
  -H "Authorization: Bearer mk_live_..." \
  -d '{
    "customer_id": "<seller_id>",
    "plan_id": "<power_seller_plan_id>"
  }'
```

See [Subscription Lifecycle](/guides/subscription-lifecycle) for how the final invoice on the old subscription is generated before the new one takes over.

***

## Refunds and disputes

When a buyer is refunded after the billing period has closed, both the seller's commission invoice and the seller's payout bill may need reversing. The public API supports two operations on finalised invoices and draft bills:

* `POST /v1/invoices/<invoice_id>/void` — voids an unpaid invoice. Fires `invoice.voided`.
* Void bills from **Dashboard → Bills** before payout is initiated.

If the payout has already been transferred, the provider-side reversal path fires `payout.reversed`. Subscribe to this webhook and settle the clawback against the seller's next payout in your own books.

For in-period refunds (before the billing cycle closes), the simplest pattern is to subtract from the ongoing rollups by ingesting a compensating event with negative property values:

```bash theme={null}
curl -X POST https://api.monigo.co/v1/ingest \
  -d '{
    "events": [{
      "event_name": "item_sold",
      "customer_id": "<seller_id>",
      "idempotency_key": "refund_<refund_id>",
      "timestamp": "2026-04-16T15:00:00Z",
      "properties": {
        "original_sale_id": "<sale_id>",
        "commission_amount": -2500,
        "seller_share": -22500
      }
    }]
  }'
```

The `sum` aggregation respects negative property values, so the rollups for `platform_commission` and `seller_earnings` drop by the refunded amount before the period closes. Use a distinct `idempotency_key` prefix (e.g., `refund_`) so refunds and sales can't collide.

***

## Alternative: seller-collects model

If you don't want to be merchant of record, each seller can use their own Paystack or Flutterwave account. Buyers pay sellers directly; Monigo only bills sellers for platform fees.

In this model, drop the payout plan and subscription entirely. Each seller has just a collection subscription, connects their own payment processor in the dashboard, and Monigo charges them at period end from their connected account.

This is operationally simpler (no merchant-of-record obligations, no held funds, no payout step) but gives you less control over buyer experience and refund timing. Pick it when your sellers already run their own payment accounts.

***

## Webhooks to subscribe to

| Event                    | What it signals                                                  |
| ------------------------ | ---------------------------------------------------------------- |
| `invoice.finalized`      | Seller's monthly fees invoice is ready                           |
| `invoice.paid`           | Seller paid their listing + commission fees                      |
| `invoice.voided`         | An invoice was voided — used for refund reconciliation           |
| `payout.success`         | A bill transfer completed successfully                           |
| `payout.failed`          | A bill transfer failed — surface to the seller and retry         |
| `payout.reversed`        | A completed transfer was reversed by the provider                |
| `subscription.suspended` | Seller was suspended after failed dunning — pause their listings |

See [Webhooks](/integrations/webhooks) for payload shapes and signature verification.

***

## Related

* [Metering](/guides/metering) — how events become usage rollups and line items
* [Pricing Models](/guides/pricing-models) — full reference for `flat_unit`, `tiered`, `package`, and other models
* [Subscription Lifecycle](/guides/subscription-lifecycle) — pausing a seller, plan changes, cancellation
* [Event-Driven Payouts](/guides/event-driven-payouts) — deeper background on the collection/payout plan split
* [Idempotency](/guides/idempotency) — why every listing, sale, and refund needs a stable idempotency key
