Manage Your Mandates
List, approve, schedule, and immediately post mandate transactions for UAE multi-payment types.
Goal: List, approve, schedule, and immediately post mandate transactions after the payer authorizes consent.
Estimated time: 20 minutes
Prerequisites
- An active mandate from Setup Your First Mandate (or an equivalent create plus bank consent flow)
- App ID, API key, and private key (for
x-signatureon signed endpoints) - A short-lived access token from Make Your First Payment
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
| Family | At mandate creation | Your action before execution |
|---|---|---|
| Fixed Periodic / Fixed Defined | Transactions created and approved | None for amount; Spare executes on the scheduled date |
| Variable Periodic / Variable Defined | Transactions created, unapproved | List β PATCH /mandate/approve with mandateId, transactionId, and amount β€ create-time max |
| Fixed On Demand | No transactions scheduled | POST /mandate/schedule (mandateId, executionDate; no amount) |
| Variable On Demand | No transactions scheduled | POST /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 usesPOST /mandate/schedule. - Approval and schedule amounts must stay within the create-time
maxAmountceiling. - Call
POST /payment-actions/post-mandateto execute immediately instead of waiting for the schedule or batch window. - Track final payment status through Webhooks.
Continue learning
Setup Your First Mandate
Create a mandate and complete bank consent.
Mandates
Mandate model and consent lifecycle.
Mandates API
List, approve, and schedule endpoints.
Payments API
Post-mandate and payment records.
Webhooks
Event delivery and signature verification.
Payment Types
Multi-payment families and field rules.