Mandates
Recurring and future-dated payments through mandate scheduling and approval.
Mandates let a payer authorize multiple payments under one consent, for subscriptions, installment plans, or future-dated transfers. Understanding mandates is essential if you use any payment type other than SingleInstantPayment.
In this guide
- When to use mandates vs single instant payments
- Payment types that create mandates
- The schedule β approve β execute cycle
- Mandate statuses and recovery from failures
Mandates vs single payments
| Single instant payment | Mandate-based payment | |
|---|---|---|
| Authorization | Once, for one amount | Once, for a series or on-demand debits |
| Use case | Checkout, invoices | Subscriptions, payroll schedules |
| API surface | Payment request + consent | + /mandate/schedule, /mandate/approve |
| Execution | Automatic after consent | Scheduled jobs or explicit post |
Payment types that use mandates
These types are declared on the payment request instructions:
| Payment type | Behavior |
|---|---|
FixedPeriodicSchedule | Fixed amount on a recurring cadence |
VariablePeriodicSchedule | Variable amount within limits on a cadence |
FixedOnDemand | Fixed amount; you trigger each debit |
VariableOnDemand | Variable amount; you trigger each debit |
FixedDefinedSchedule | Fixed amounts on specific future dates |
VariableDefinedSchedule | Variable amounts on specific future dates |
SingleInstantPayment does not create a mandate, see Payment flow.
Mandate lifecycle
Status reference
| Status | Meaning |
|---|---|
Pending | Mandate created; awaiting first schedule or approval |
Active | Authorized; ready for execution windows |
Running | At least one transaction has been submitted |
Completed | All scheduled payments fulfilled |
Suspended | A debit failed; awaiting retry or intervention |
Revoked | Cancelled by payer or merchant |
Expired | Past end date or parent consent expired |
On-demand flow
For VariableOnDemand or FixedOnDemand, the payer authorizes a ceiling; you trigger individual debits:
Step 1, Schedule
curl -X POST https://api.sandbox.tryspare.ae/mandate/schedule \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "x-tenant: UAE" \
-H "Content-Type: application/json" \
-d '{
"mandateId": "MANDATE_ID",
"executionDate": "2026-07-01",
"amount": "500.00"
}'const scheduled = await client.mandates.schedule({
amount: 500_00,
currency: "AED",
executionDate: "2026-07-01",
description: "Monthly subscription",
});scheduled = client.mandates.schedule(
amount=500_00,
currency="AED",
execution_date="2026-07-01",
description="Monthly subscription",
)Configuration config = Configuration.builder()
.appId(System.getenv("SPARE_APP_ID"))
.apiKey(System.getenv("SPARE_API_KEY"))
.tenant("UAE")
.environment("sandbox")
.build();
SpareApiClient client = SpareApiClient.fromConfiguration(config);
var scheduled = client.mandates().schedule(ScheduleMandateRequest.builder()
.amount(500_00)
.currency("AED")
.executionDate("2026-07-01")
.description("Monthly subscription")
.build());var client = new SpareApiClient(new Configuration(new ConfigurationOptions
{
AppId = Environment.GetEnvironmentVariable("SPARE_APP_ID")!,
ApiKey = Environment.GetEnvironmentVariable("SPARE_API_KEY")!,
Tenant = "UAE",
Environment = "sandbox",
}));
var scheduled = await client.Mandates.ScheduleAsync(new ScheduleMandateRequest
{
Amount = 500_00,
Currency = "AED",
ExecutionDate = "2026-07-01",
Description = "Monthly subscription",
});config, err := spareapi.NewConfiguration(spareapi.ConfigurationOptions{
AppID: os.Getenv("SPARE_APP_ID"),
APIKey: os.Getenv("SPARE_API_KEY"),
Tenant: "UAE",
Environment: "sandbox",
})
if err != nil {
log.Fatal(err)
}
client := spareapi.NewClientFromConfiguration(config)
scheduled, err := client.Mandates.Schedule(ctx, &spareapi.ScheduleMandateRequest{
Amount: 500_00,
Currency: "AED",
ExecutionDate: "2026-07-01",
Description: "Monthly subscription",
})
if err != nil {
log.Fatal(err)
}Step 2, Approve
Required for variable on-demand amounts or when your product separates approval from scheduling:
curl -X PATCH https://api.sandbox.tryspare.ae/mandate/approve \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "x-tenant: UAE" \
-H "Content-Type: application/json" \
-d '{
"mandateId": "MANDATE_ID",
"transactionId": "TRANSACTION_ID",
"amount": "500.00",
"executionDate": "2026-07-01"
}'await client.mandates.approve({ mandateId: scheduled.data.id });client.mandates.approve(mandate_id=scheduled.data.id)client.mandates().approve(ApproveMandateRequest.builder()
.mandateId(scheduled.getData().getId())
.build());await client.Mandates.ApproveAsync(new ApproveMandateRequest
{
MandateId = scheduled.Data.Id,
});_, err := client.Mandates.Approve(ctx, &spareapi.ApproveMandateRequest{
MandateID: scheduled.Data.Id,
})
if err != nil {
log.Fatal(err)
}Step 3, Monitor transactions
curl "https://api.sandbox.tryspare.ae/mandate/MANDATE_ID/transactions" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "x-tenant: UAE"const txs = await client.mandates.listTransactions({
mandateId: scheduled.data.id,
});txs = client.mandates.list_transactions(mandate_id=scheduled.data.id)var txs = client.mandates().listTransactions(ListMandateTransactionsRequest.builder()
.mandateId(scheduled.getData().getId())
.build());var txs = await client.Mandates.ListTransactionsAsync(new ListMandateTransactionsRequest
{
MandateId = scheduled.Data.Id,
});txs, err := client.Mandates.ListTransactions(ctx, &spareapi.ListMandateTransactionsRequest{
MandateID: scheduled.Data.Id,
})
if err != nil {
log.Fatal(err)
}Periodic and defined schedules
For FixedPeriodicSchedule and similar types, the payer grants consent once during bank authorization. Subsequent debits run on the defined cadence without per-payment payer approval, subject to consent limits (max amount, count, end date).
Your integration focus shifts to:
- Correctly defining
instructionson the payment request - Monitoring mandate status and transactions
- Handling
Suspendedwhen a debit fails
Limits and validation
Mandates enforce boundaries set at authorization time:
- Maximum cumulative amount, total debits cannot exceed the authorized cap
- Maximum number of payments, count limit for the mandate period
- Start and end dates, execution only within the authorized window
- Currency, must remain consistent (e.g. AED in UAE)
Violations return API errors at schedule or execution time.
Failure recovery
| Situation | Action |
|---|---|
Debit fails (Suspended) | Investigate with transaction details; fix amount/account; reactivate if supported |
| Consent expires mid-mandate | Collect new payer authorization |
| Payer revokes | Stop scheduling; mark subscription cancelled in your system |
When to use mandates
- Subscriptions,
FixedPeriodicSchedulewith monthly cadence - Installment plans,
FixedDefinedSchedulewith known dates - Usage-based billing,
VariableOnDemandwith monthly approve + schedule - Invoice on demand,
FixedOnDemandafter prior consent
Related resources
- Mandate Scheduling guide
- Mandates API
- Consent lifecycle, parent consent expiry
Key takeaways
- Mandates bundle multiple debits under one payer authorization.
- On-demand types need explicit
scheduleand oftenapproveper debit. - Periodic types auto-execute within consent limits after initial authorization.