_Docs/
Get StartedModulesPlatformDeployCookbookChangelogReference
_Cookbook
  • Introduction
    • RideShare Tutorial
    • Omnibus Account Management
    • Payment Card Acceptance Processing
    • Card Issuing & Financial Host
    • Stablecoin On-Ramp & Off-Ramp Operations
  1. Examples
  2. Getting Started
  3. RideShare Tutorial
Cookbook

RideShare Tutorial

In this tutorial, we will set up and use Formance Ledger for a ride-sharing platform called "RideShare." We will design a Chart of Accounts, create a schema to enforce it, use Numscript to execute transactions, and check account balances.

Prerequisites
fctl CLI installedA sandbox stack created

The Complete Schema#

Here is the full ledger schema for the RideShare tutorial. It defines the account hierarchy and all four transaction patterns we'll walk through below.

Ledger Schema4 transactions
├─ worldaccount
├─ rider
│ └─ $riderId
│ └─ ride
│ └─ $rideId
│ └─ paymentaccount
├─ ride
│ └─ $rideId
│ ├─ mainaccount
│ └─ feesaccount
└─ driver
└─ $driverId
├─ ride
│ └─ $rideIdaccount
└─ mainaccount

Design the Chart of Accounts#

The Chart of Accounts should reflect all account types relevant to your flow of funds and reporting needs.

We recommend starting with the key questions you need the ledger to answer. For RideShare:

  • How much did we pay the drivers?
  • How much did we collect in service fees?
  • How much did an individual rider pay?
  • How much did an individual driver earn?

And key events the ledger must support:

  • Rider books a ride
  • RideShare confirms the ride
  • Driver finishes the ride
  • RideShare completes ride verification
  • Driver requests earnings withdrawal

The schema defines four account groups to support these questions and events:

World#

└─ worldaccount

The @world account represents the external world — funds entering and exiting the ledger.

Rider#

└─ rider
└─ $riderId
└─ ride
└─ $rideId
└─ paymentaccount

Each rider gets a per-ride payment account (rider:$riderId:ride:$rideId:payment) that records the estimated charge for that ride.

Ride#

└─ ride
└─ $rideId
├─ mainaccount
└─ feesaccount

Each ride has a main account (total charged) and a fees account (RideShare's service fee).

Driver#

└─ driver
└─ $driverId
├─ ride
│ └─ $rideIdaccount
└─ mainaccount

Each driver has per-ride earning accounts and a main account that accumulates total earnings across rides.

The structured naming convention helps organize accounts into segments, making it easier to manage and query them.

Create the Schema#

Now let's create the schema to enforce the Chart of Accounts. First, get your sandbox API endpoint:

Bash
fctl stack show

Then create the schema using the API. You can copy the YAML from the schema viewer above, or use the JSON equivalent:

curl -X POST $FORMANCE_API_URL/api/ledger/v2/default/schemas/v1.0.0 \
  -H "Content-Type: application/json" \
  -d '{
    "chart": {
      "world": {},
      "rider": {
        "$riderId": {
          "ride": {
            "$rideId": {
              "payment": {}
            }
          }
        }
      },
      "ride": {
        "$rideId": {
          "main": {},
          "fees": {}
        }
      },
      "driver": {
        "$driverId": {
          "ride": {
            "$rideId": {}
          },
          "main": {}
        }
      }
    },
    "transactions": {}
  }'
POST/api/ledger/v2/default/schemas/v1.0.0

By default, the ledger runs in audit mode—transactions are validated against your schema but allowed through. For strict enforcement, see enforcement modes.

Introduce Money into the Ledger#

When a rider books a ride, we record the estimated payment from @world into the rider's ride payment account.

RIDE_BOOKING
vars {
    number $amount
    account $riderId
    account $rideId
}

send [USD/2 $amount] (
    source = @world
    destination = @rider:$riderId:ride:$rideId:payment
)

Execute this transaction:

fctl ledger send default --numscript '{script}'
POST/api/ledger/v2/default/transactions

This adds $20 to the @rider:xx:ride:yy:payment account.

Run Transactions on the Ledger#

Ride Confirmation#

When RideShare confirms the ride, transfer the payment from the rider's account to the ride's main account.

RIDE_CONFIRMATION
vars {
    number $amount
    account $riderId
    account $rideId
}

send [USD/2 $amount] (
    source = @rider:$riderId:ride:$rideId:payment
    destination = @ride:$rideId:main
)
fctl ledger send default --numscript '{script}'
POST/api/ledger/v2/default/transactions

Ride Completion#

After the ride is completed, split the payment between the driver's earnings and RideShare's service fees.

RIDE_COMPLETION
vars {
    number $amount
    account $rideId
    account $driverId
}

send [USD/2 $amount] (
    source = @ride:$rideId:main
    destination = {
        10% to @ride:$rideId:fees
        remaining to @driver:$driverId:main
    }
)
fctl ledger send default --numscript '{script}'
POST/api/ledger/v2/default/transactions

Driver Payout#

After some days, the driver requests their earnings. Transfer all accumulated earnings back to @world to represent funds leaving the ledger. The wildcard * sends the entire balance.

DRIVER_PAYOUT
vars {
    account $driverId
}

send [USD/2 *] (
    source = @driver:$driverId:main
    destination = @world
)
fctl ledger send default --numscript '{script}'
POST/api/ledger/v2/default/transactions

Check Account Balances#

You can check the balances of the accounts using the Formance Console.

IntroductionOmnibus Account Management
On This Page
  • The Complete Schema
  • Design the Chart of Accounts
  • World
  • Rider
  • Ride
  • Driver
  • Create the Schema
  • Introduce Money into the Ledger
  • Run Transactions on the Ledger
  • Ride Confirmation
  • Ride Completion
  • Driver Payout
  • Check Account Balances