SpareSpare Docs
GuidesAPI Reference

Manage Your Mandates

List, approve, schedule, and immediately post mandate transactions for UAE multi-payment types.

Integration guide

Goal: List, approve, schedule, and immediately post mandate transactions after the payer authorizes consent.

Estimated time: 20 minutes

Prerequisites

After the payer authorizes consent, Spare creates a mandate. What you do next depends on whether Spare pre-created transactions and whether amounts are fixed or variable.

Controls by Payment Family

FamilyAt mandate creationYour action before execution
Fixed Periodic / Fixed DefinedTransactions created and approvedNone for amount; Spare executes on the scheduled date
Variable Periodic / Variable DefinedTransactions created, unapprovedList β†’ PATCH /mandate/approve with mandateId, transactionId, and amount ≀ create-time max
Fixed On DemandNo transactions scheduledPOST /mandate/schedule (mandateId, executionDate; no amount)
Variable On DemandNo transactions scheduledPOST /mandate/schedule; if amount is set, the transaction is treated as approved

Variable Periodic and Variable Defined use PATCH /mandate/approve, not POST /mandate/schedule, to set the debit amount.

List Mandate Transactions

For Periodic and Defined Schedule types, Spare pre-creates transactions when the mandate becomes active. Call GET /mandate/{mandateId}/transactions to read scheduled debits, approval state, and per-transaction limits before you approve or post.

Replace MANDATE_ID with the mandate UUID from Setup Your First Mandate: the mandateId query parameter on the signed success redirect.

curl "https://api.sandbox.tryspare.ae/mandate/MANDATE_ID/transactions" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "x-tenant: UAE"
const res = await fetch(
  `https://api.sandbox.tryspare.ae/mandate/${mandateId}/transactions`,
  {
    headers: {
      Authorization: `Bearer ${accessToken}`,
      "x-tenant": "UAE",
    },
  },
);

const { data } = await res.json();
import requests

res = requests.get(
    f"https://api.sandbox.tryspare.ae/mandate/{mandate_id}/transactions",
    headers={
        "Authorization": f"Bearer {access_token}",
        "x-tenant": "UAE",
    },
)

data = res.json()["data"]
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create(
        "https://api.sandbox.tryspare.ae/mandate/" + mandateId + "/transactions"))
    .header("Authorization", "Bearer " + accessToken)
    .header("x-tenant", "UAE")
    .GET()
    .build();

HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
// Parse data[] from the JSON response.
using var client = new HttpClient();
var request = new HttpRequestMessage(
    HttpMethod.Get,
    $"https://api.sandbox.tryspare.ae/mandate/{mandateId}/transactions");
request.Headers.Add("Authorization", $"Bearer {accessToken}");
request.Headers.Add("x-tenant", "UAE");

var response = await client.SendAsync(request);
// Parse data[] from the JSON response.
req, err := http.NewRequest(
    http.MethodGet,
    "https://api.sandbox.tryspare.ae/mandate/"+mandateId+"/transactions",
    nil,
)
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("x-tenant", "UAE")

res, err := http.DefaultClient.Do(req)
// Parse data[] from the JSON response.

Each item in data includes id (use this value as transactionId in approve and post calls), isApproved, maxAmount, executionDate, and status.

Approve Variable Scheduled Transactions

For Variable Periodic and Variable Defined types, Spare creates transactions in an unapproved state because the debit amount is not known at mandate creation. Call PATCH /mandate/approve with the transaction id from the list response, the mandate UUID, and the amount you want to debit.

The amount must be less than or equal to maxAmount on the payment request at create time (and to maxAmount on the transaction when present). Spare rejects amounts above that ceiling.

Request signature required

PATCH /mandate/approve requires an x-signature header. See Request signing for the canonical serialization rules.

Sign the JSON body, then send that same object as the HTTP body with x-signature.

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" \
  -H "x-signature: $X_SIGNATURE" \
  -d '{
    "mandateId": "MANDATE_ID",
    "transactionId": "TRANSACTION_ID",
    "amount": 500.00
  }'
const approveBody = {
  mandateId: mandateId,
  transactionId: transactionId,
  amount: 500.0,
};

// Build xSignature from approveBody (see Request signing).

const res = await fetch("https://api.sandbox.tryspare.ae/mandate/approve", {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${accessToken}`,
    "x-tenant": "UAE",
    "Content-Type": "application/json",
    "x-signature": xSignature,
  },
  body: JSON.stringify(approveBody),
});

const { data } = await res.json();
import json
import requests

approve_body = {
    "mandateId": mandate_id,
    "transactionId": transaction_id,
    "amount": 500.00,
}

# Build x_signature from approve_body (see Request signing).

res = requests.patch(
    "https://api.sandbox.tryspare.ae/mandate/approve",
    headers={
        "Authorization": f"Bearer {access_token}",
        "x-tenant": "UAE",
        "Content-Type": "application/json",
        "x-signature": x_signature,
    },
    data=json.dumps(approve_body),
)

data = res.json()["data"]
String approveJson = """
    {
      "mandateId": "MANDATE_ID",
      "transactionId": "TRANSACTION_ID",
      "amount": 500.00
    }
    """;

// Build xSignature from approveJson (see Request signing).

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.sandbox.tryspare.ae/mandate/approve"))
    .header("Authorization", "Bearer " + accessToken)
    .header("x-tenant", "UAE")
    .header("Content-Type", "application/json")
    .header("x-signature", xSignature)
    .method("PATCH", HttpRequest.BodyPublishers.ofString(approveJson))
    .build();

HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
// Parse data from the JSON response.
var approveBody = new
{
    mandateId = mandateId,
    transactionId = transactionId,
    amount = 500.00,
};

// Build xSignature from approveBody (see Request signing).
var body = JsonSerializer.Serialize(approveBody);

using var client = new HttpClient();
var request = new HttpRequestMessage(
    HttpMethod.Patch,
    "https://api.sandbox.tryspare.ae/mandate/approve");
request.Headers.Add("Authorization", $"Bearer {accessToken}");
request.Headers.Add("x-tenant", "UAE");
request.Headers.Add("x-signature", xSignature);
request.Content = new StringContent(body, Encoding.UTF8, "application/json");

var response = await client.SendAsync(request);
// Parse data from the JSON response.
approveBody := map[string]any{
    "mandateId":     mandateId,
    "transactionId": transactionId,
    "amount":        500.00,
}

// Build xSignature from approveBody (see Request signing).
body, _ := json.Marshal(approveBody)

req, err := http.NewRequest(
    http.MethodPatch,
    "https://api.sandbox.tryspare.ae/mandate/approve",
    bytes.NewReader(body),
)
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("x-tenant", "UAE")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("x-signature", xSignature)

res, err := http.DefaultClient.Do(req)
// Parse data from the JSON response.

The schema also accepts optional executionDate when you need to move the debit date. Omit it to keep the date Spare assigned at creation.

Schedule On-Demand Transactions

For Fixed On Demand and Variable On Demand types, no transactions exist until you schedule one. Call POST /mandate/schedule with mandateId and executionDate. The date may be today or a future date.

For Fixed On Demand, omit amount. For Variable On Demand, amount is optional: when you include it, Spare treats the transaction as approved for that amount.

When you schedule for today without an immediate post, Spare waits for the next payment-engine batch before execution. Use Post a Transaction Immediately to debit right away.

Request signature required

POST /mandate/schedule requires an x-signature header. See Request signing.

For Fixed On Demand, send only mandateId and executionDate. Do not include amount.

The example below schedules a Variable On Demand debit with an approved amount.

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" \
  -H "x-signature: $X_SIGNATURE" \
  -d '{
    "mandateId": "MANDATE_ID",
    "executionDate": "2026-08-20T00:00:00Z",
    "amount": 35.00
  }'
const scheduleBody = {
  mandateId: mandateId,
  executionDate: "2026-08-20T00:00:00Z",
  amount: 35.0,
};

// Build xSignature from scheduleBody (see Request signing).

const res = await fetch("https://api.sandbox.tryspare.ae/mandate/schedule", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${accessToken}`,
    "x-tenant": "UAE",
    "Content-Type": "application/json",
    "x-signature": xSignature,
  },
  body: JSON.stringify(scheduleBody),
});

const { data } = await res.json();
const transactionId = data.id;
import json
import requests

schedule_body = {
    "mandateId": mandate_id,
    "executionDate": "2026-08-20T00:00:00Z",
    "amount": 35.00,
}

# Build x_signature from schedule_body (see Request signing).

res = requests.post(
    "https://api.sandbox.tryspare.ae/mandate/schedule",
    headers={
        "Authorization": f"Bearer {access_token}",
        "x-tenant": "UAE",
        "Content-Type": "application/json",
        "x-signature": x_signature,
    },
    data=json.dumps(schedule_body),
)

data = res.json()["data"]
transaction_id = data["id"]
String scheduleJson = """
    {
      "mandateId": "MANDATE_ID",
      "executionDate": "2026-08-20T00:00:00Z",
      "amount": 35.00
    }
    """;

// Build xSignature from scheduleJson (see Request signing).

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.sandbox.tryspare.ae/mandate/schedule"))
    .header("Authorization", "Bearer " + accessToken)
    .header("x-tenant", "UAE")
    .header("Content-Type", "application/json")
    .header("x-signature", xSignature)
    .POST(HttpRequest.BodyPublishers.ofString(scheduleJson))
    .build();

HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
// Parse data.id as the transactionId for post-mandate.
var scheduleBody = new
{
    mandateId = mandateId,
    executionDate = "2026-08-20T00:00:00Z",
    amount = 35.00,
};

// Build xSignature from scheduleBody (see Request signing).
var body = JsonSerializer.Serialize(scheduleBody);

using var client = new HttpClient();
var request = new HttpRequestMessage(
    HttpMethod.Post,
    "https://api.sandbox.tryspare.ae/mandate/schedule");
request.Headers.Add("Authorization", $"Bearer {accessToken}");
request.Headers.Add("x-tenant", "UAE");
request.Headers.Add("x-signature", xSignature);
request.Content = new StringContent(body, Encoding.UTF8, "application/json");

var response = await client.SendAsync(request);
// Parse data.id as the transactionId for post-mandate.
scheduleBody := map[string]any{
    "mandateId":     mandateId,
    "executionDate": "2026-08-20T00:00:00Z",
    "amount":        35.00,
}

// Build xSignature from scheduleBody (see Request signing).
body, _ := json.Marshal(scheduleBody)

req, err := http.NewRequest(
    http.MethodPost,
    "https://api.sandbox.tryspare.ae/mandate/schedule",
    bytes.NewReader(body),
)
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("x-tenant", "UAE")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("x-signature", xSignature)

res, err := http.DefaultClient.Do(req)
// Parse data.id as the transactionId for post-mandate.

The response returns data.id as the new transaction identifier.

Post a Transaction Immediately

After you schedule an on-demand transaction, or when you need execution before the scheduled date, call POST /payment-actions/post-mandate. Include mandateId, transactionId (from list or schedule), and consentId from the payment consent linked to the mandate.

consentId is the payment consent UUID linked to the mandate. After authorization, read it from GET /consent/payment/sync?paymentRequestId= or GET /consent/payment/list?externalReference= (see Setup). Use GET /consent/payment/{consentId} to re-fetch the consent record before you post.

Without this call, Spare executes approved transactions on the scheduled executionDate or in the next payment-engine batch for same-day schedules.

curl -X POST https://api.sandbox.tryspare.ae/payment-actions/post-mandate \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "x-tenant: UAE" \
  -H "Content-Type: application/json" \
  -d '{
    "mandateId": "MANDATE_ID",
    "transactionId": "TRANSACTION_ID",
    "consentId": "CONSENT_ID"
  }'
const postBody = {
  mandateId: mandateId,
  transactionId: transactionId,
  consentId: consentId,
};

const res = await fetch(
  "https://api.sandbox.tryspare.ae/payment-actions/post-mandate",
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${accessToken}`,
      "x-tenant": "UAE",
      "Content-Type": "application/json",
    },
    body: JSON.stringify(postBody),
  },
);

const { data } = await res.json();
import json
import requests

post_body = {
    "mandateId": mandate_id,
    "transactionId": transaction_id,
    "consentId": consent_id,
}

res = requests.post(
    "https://api.sandbox.tryspare.ae/payment-actions/post-mandate",
    headers={
        "Authorization": f"Bearer {access_token}",
        "x-tenant": "UAE",
        "Content-Type": "application/json",
    },
    data=json.dumps(post_body),
)

data = res.json()["data"]
String postJson = """
    {
      "mandateId": "MANDATE_ID",
      "transactionId": "TRANSACTION_ID",
      "consentId": "CONSENT_ID"
    }
    """;

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.sandbox.tryspare.ae/payment-actions/post-mandate"))
    .header("Authorization", "Bearer " + accessToken)
    .header("x-tenant", "UAE")
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString(postJson))
    .build();

HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
// Parse data from the JSON response.
var postBody = new
{
    mandateId = mandateId,
    transactionId = transactionId,
    consentId = consentId,
};

var body = JsonSerializer.Serialize(postBody);

using var client = new HttpClient();
var request = new HttpRequestMessage(
    HttpMethod.Post,
    "https://api.sandbox.tryspare.ae/payment-actions/post-mandate");
request.Headers.Add("Authorization", $"Bearer {accessToken}");
request.Headers.Add("x-tenant", "UAE");
request.Content = new StringContent(body, Encoding.UTF8, "application/json");

var response = await client.SendAsync(request);
// Parse data from the JSON response.
postBody := map[string]string{
    "mandateId":     mandateId,
    "transactionId": transactionId,
    "consentId":     consentId,
}

body, _ := json.Marshal(postBody)

req, err := http.NewRequest(
    http.MethodPost,
    "https://api.sandbox.tryspare.ae/payment-actions/post-mandate",
    bytes.NewReader(body),
)
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("x-tenant", "UAE")
req.Header.Set("Content-Type", "application/json")

res, err := http.DefaultClient.Do(req)
// Parse data from the JSON response.

Status Updates via Webhooks

Spare notifies you of payment status changes through webhooks. Subscribe to payment events and verify delivery signatures on your server. See Webhooks for event types and verification steps.

Key takeaways

  • Controls differ by payment family: Periodic/Defined pre-create transactions; On Demand requires scheduling.
  • Variable Periodic and Variable Defined use PATCH /mandate/approve; On Demand uses POST /mandate/schedule.
  • Approval and schedule amounts must stay within the create-time maxAmount ceiling.
  • Call POST /payment-actions/post-mandate to execute immediately instead of waiting for the schedule or batch window.
  • Track final payment status through Webhooks.

On this page