SpareSpare Docs
GuidesAPI Reference

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 paymentMandate-based payment
AuthorizationOnce, for one amountOnce, for a series or on-demand debits
Use caseCheckout, invoicesSubscriptions, payroll schedules
API surfacePayment request + consent+ /mandate/schedule, /mandate/approve
ExecutionAutomatic after consentScheduled jobs or explicit post

Payment types that use mandates

These types are declared on the payment request instructions:

Payment typeBehavior
FixedPeriodicScheduleFixed amount on a recurring cadence
VariablePeriodicScheduleVariable amount within limits on a cadence
FixedOnDemandFixed amount; you trigger each debit
VariableOnDemandVariable amount; you trigger each debit
FixedDefinedScheduleFixed amounts on specific future dates
VariableDefinedScheduleVariable amounts on specific future dates

SingleInstantPayment does not create a mandate, see Payment flow.

Mandate lifecycle

Status reference

StatusMeaning
PendingMandate created; awaiting first schedule or approval
ActiveAuthorized; ready for execution windows
RunningAt least one transaction has been submitted
CompletedAll scheduled payments fulfilled
SuspendedA debit failed; awaiting retry or intervention
RevokedCancelled by payer or merchant
ExpiredPast 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 instructions on the payment request
  • Monitoring mandate status and transactions
  • Handling Suspended when 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

SituationAction
Debit fails (Suspended)Investigate with transaction details; fix amount/account; reactivate if supported
Consent expires mid-mandateCollect new payer authorization
Payer revokesStop scheduling; mark subscription cancelled in your system

When to use mandates

  • Subscriptions, FixedPeriodicSchedule with monthly cadence
  • Installment plans, FixedDefinedSchedule with known dates
  • Usage-based billing, VariableOnDemand with monthly approve + schedule
  • Invoice on demand, FixedOnDemand after prior consent

Key takeaways

  • Mandates bundle multiple debits under one payer authorization.
  • On-demand types need explicit schedule and often approve per debit.
  • Periodic types auto-execute within consent limits after initial authorization.

On this page