SpareSpare Docs
GuidesAPI Reference

Account Information

Integrate account requests, consents, authorization, sync, and risk reports.

Integration guide

Goal: Obtain customer consent for read access to their bank data, sync account information, and optionally generate a risk report.

Estimated time: 30 minutes

Prerequisites

  • Sandbox credentials (appId, apiKey, tenant UAE)
  • An AIS subscription enabled on your Spare account (see Coverage)

Concept first

Read Account Information for the request β†’ consent β†’ authorize β†’ sync model before integrating.

Account Information in the UAE follows a core path: create an account request, create an account consent, complete bank authorization, run a bulk sync, and poll until sync completes. You can then generate a risk report from the synced data. All calls use Authorization: Bearer and x-tenant: UAE.

Authenticate

Exchange your API credentials for a Bearer access token. See Quick Start Setup or the Authentication reference.

curl -X POST https://api.sandbox.tryspare.ae/auth/api-keys/sessions \
  -H "x-tenant: UAE" \
  -H "app-id: $SPARE_APP_ID" \
  -H "x-api-key: $SPARE_API_KEY"
const response = await fetch("https://api.sandbox.tryspare.ae/auth/api-keys/sessions", {
  method: "POST",
  headers: {
    "x-tenant": "UAE",
    "app-id": process.env.SPARE_APP_ID!,
    "x-api-key": process.env.SPARE_API_KEY!,
  },
});
const { data } = await response.json();
const accessToken = data.accessToken as string;
import os
import requests

response = requests.post(
    "https://api.sandbox.tryspare.ae/auth/api-keys/sessions",
    headers={
        "x-tenant": "UAE",
        "app-id": os.environ["SPARE_APP_ID"],
        "x-api-key": os.environ["SPARE_API_KEY"],
    },
)
response.raise_for_status()
access_token = response.json()["data"]["accessToken"]
var request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.sandbox.tryspare.ae/auth/api-keys/sessions"))
    .header("x-tenant", "UAE")
    .header("app-id", System.getenv("SPARE_APP_ID"))
    .header("x-api-key", System.getenv("SPARE_API_KEY"))
    .POST(HttpRequest.BodyPublishers.noBody())
    .build();
var response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post,
    "https://api.sandbox.tryspare.ae/auth/api-keys/sessions");
request.Headers.Add("x-tenant", "UAE");
request.Headers.Add("app-id", Environment.GetEnvironmentVariable("SPARE_APP_ID"));
request.Headers.Add("x-api-key", Environment.GetEnvironmentVariable("SPARE_API_KEY"));
var response = await client.SendAsync(request);
req, _ := http.NewRequest(http.MethodPost,
  "https://api.sandbox.tryspare.ae/auth/api-keys/sessions", nil)
req.Header.Set("x-tenant", "UAE")
req.Header.Set("app-id", os.Getenv("SPARE_APP_ID"))
req.Header.Set("x-api-key", os.Getenv("SPARE_API_KEY"))
resp, err := http.DefaultClient.Do(req)

Create an account request

Define the data permissions and consent window for the customer's authorization. Store the returned id as your requestId for the consent step.

  • consentExpiryDate, successRedirectUrl, and failureRedirectUrl are optional.
  • When you omit consentExpiryDate, Spare sets consent expiry to one week from today's date.
curl -X POST https://api.sandbox.tryspare.ae/data/account-requests \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "x-tenant: UAE" \
  -H "Content-Type: application/json" \
  -d '{
    "consentExpiryDate": "2026-12-31T23:59:59.000Z",
    "countryCode": "AE",
    "requestPermissions": ["ReadAccountsBasic", "ReadAccountsDetail", "ReadBalances", "ReadTransactionsDetail"],
    "successRedirectUrl": "https://example.com/ais/success",
    "failureRedirectUrl": "https://example.com/ais/failure"
  }'
const response = await fetch("https://api.sandbox.tryspare.ae/data/account-requests", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${accessToken}`,
    "x-tenant": "UAE",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    consentExpiryDate: "2026-12-31T23:59:59.000Z",
    countryCode: "AE",
    requestPermissions: [
      "ReadAccountsBasic",
      "ReadAccountsDetail",
      "ReadBalances",
      "ReadTransactionsDetail",
    ],
    successRedirectUrl: "https://example.com/ais/success",
    failureRedirectUrl: "https://example.com/ais/failure",
  }),
});
const { data: accountRequest } = await response.json();
response = requests.post(
    "https://api.sandbox.tryspare.ae/data/account-requests",
    headers={
        "Authorization": f"Bearer {access_token}",
        "x-tenant": "UAE",
    },
    json={
        "consentExpiryDate": "2026-12-31T23:59:59.000Z",
        "countryCode": "AE",
        "requestPermissions": [
            "ReadAccountsBasic",
            "ReadAccountsDetail",
            "ReadBalances",
            "ReadTransactionsDetail",
        ],
        "successRedirectUrl": "https://example.com/ais/success",
        "failureRedirectUrl": "https://example.com/ais/failure",
    },
)
response.raise_for_status()
account_request = response.json()["data"]
var body = """
  {"consentExpiryDate":"2026-12-31T23:59:59.000Z","countryCode":"AE",
   "requestPermissions":["ReadAccountsBasic","ReadAccountsDetail","ReadBalances","ReadTransactionsDetail"],
   "successRedirectUrl":"https://example.com/ais/success","failureRedirectUrl":"https://example.com/ais/failure"}
  """;
var request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.sandbox.tryspare.ae/data/account-requests"))
    .header("Authorization", "Bearer " + accessToken)
    .header("x-tenant", "UAE")
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString(body))
    .build();
var payload = new {
    consentExpiryDate = "2026-12-31T23:59:59.000Z",
    countryCode = "AE",
    requestPermissions = new[] { "ReadAccountsBasic", "ReadAccountsDetail", "ReadBalances", "ReadTransactionsDetail" },
    successRedirectUrl = "https://example.com/ais/success",
    failureRedirectUrl = "https://example.com/ais/failure",
};
using var request = new HttpRequestMessage(HttpMethod.Post,
    "https://api.sandbox.tryspare.ae/data/account-requests");
request.Headers.Add("Authorization", $"Bearer {accessToken}");
request.Headers.Add("x-tenant", "UAE");
request.Content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
body := strings.NewReader(`{"consentExpiryDate":"2026-12-31T23:59:59.000Z","countryCode":"AE",
  "requestPermissions":["ReadAccountsBasic","ReadAccountsDetail","ReadBalances","ReadTransactionsDetail"],
  "successRedirectUrl":"https://example.com/ais/success","failureRedirectUrl":"https://example.com/ais/failure"}`)
req, _ := http.NewRequest(http.MethodPost, "https://api.sandbox.tryspare.ae/data/account-requests", body)
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("x-tenant", "UAE")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)

See Account Requests for all fields and response shapes.

Link the consent to the account request and the customer's bank (providerId). Redirect the customer to the authorization URL returned in the response.

curl -X POST https://api.sandbox.tryspare.ae/consent/account \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "x-tenant: UAE" \
  -H "Content-Type: application/json" \
  -d '{
    "providerId": "00000000-0000-0000-0000-000000000001",
    "requestId": "YOUR_ACCOUNT_REQUEST_ID",
    "userType": "Retail",
    "purpose": "AccountAggregation"
  }'
const response = await fetch("https://api.sandbox.tryspare.ae/consent/account", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${accessToken}`,
    "x-tenant": "UAE",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    providerId: "00000000-0000-0000-0000-000000000001",
    requestId: accountRequest.id,
    userType: "Retail",
    purpose: "AccountAggregation",
  }),
});
const { data: consent } = await response.json();
response = requests.post(
    "https://api.sandbox.tryspare.ae/consent/account",
    headers={
        "Authorization": f"Bearer {access_token}",
        "x-tenant": "UAE",
    },
    json={
        "providerId": "00000000-0000-0000-0000-000000000001",
        "requestId": account_request["id"],
        "userType": "Retail",
        "purpose": "AccountAggregation",
    },
)
var body = String.format("""
  {"providerId":"00000000-0000-0000-0000-000000000001","requestId":"%s","userType":"Retail","purpose":"AccountAggregation"}
  """, accountRequestId);
var payload = new {
    providerId = "00000000-0000-0000-0000-000000000001",
    requestId = accountRequestId,
    userType = "Retail",
    purpose = "AccountAggregation",
};
payload := fmt.Sprintf(`{"providerId":"00000000-0000-0000-0000-000000000001",
  "requestId":"%s","userType":"Retail","purpose":"AccountAggregation"}`, accountRequestId)

See Account Consents.

Complete authorization

Send the customer to the authorization URL returned on the consent. After they approve access at their bank, Spare completes the bank callback. Poll the consent until it is authorized, then start a sync.

See Authorization and Account Consents.

Sync account data

Start a sync job for the authorized consent. Set the boolean flags for each data slice you need (accounts, balances, transactions, and related objects). The response includes a jobId you use to check progress in the next step.

curl -X POST "https://api.sandbox.tryspare.ae/data/sync?consentId=YOUR_CONSENT_ID" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "x-tenant: UAE" \
  -H "Content-Type: application/json" \
  -d '{
    "syncAccounts": true,
    "syncBalances": true,
    "syncTransactions": true
  }'
const params = new URLSearchParams({ consentId });
const response = await fetch(`https://api.sandbox.tryspare.ae/data/sync?${params}`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${accessToken}`,
    "x-tenant": "UAE",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    syncAccounts: true,
    syncBalances: true,
    syncTransactions: true,
  }),
});
const { data: { jobId } } = await response.json();
response = requests.post(
    "https://api.sandbox.tryspare.ae/data/sync",
    params={"consentId": consent_id},
    headers={"Authorization": f"Bearer {access_token}", "x-tenant": "UAE"},
    json={"syncAccounts": True, "syncBalances": True, "syncTransactions": True},
)
job_id = response.json()["data"]["jobId"]
var uri = URI.create("https://api.sandbox.tryspare.ae/data/sync?consentId=" + consentId);
var url = $"https://api.sandbox.tryspare.ae/data/sync?consentId={consentId}";
url := "https://api.sandbox.tryspare.ae/data/sync?consentId=" + consentId

See Sync.

Poll sync job status (optional)

After you start a sync job, call this endpoint on a short interval until every slice you requested reports complete. Stop polling when all slices succeed or when a slice fails so you can retry or surface an error to the user.

curl -X GET "https://api.sandbox.tryspare.ae/data/sync/YOUR_JOB_ID" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "x-tenant: UAE"
const response = await fetch(
  `https://api.sandbox.tryspare.ae/data/sync/${jobId}`,
  {
    headers: {
      Authorization: `Bearer ${accessToken}`,
      "x-tenant": "UAE",
    },
  },
);
const { data: syncStatus } = await response.json();
response = requests.get(
    f"https://api.sandbox.tryspare.ae/data/sync/{job_id}",
    headers={"Authorization": f"Bearer {access_token}", "x-tenant": "UAE"},
)
response.raise_for_status()
sync_status = response.json()["data"]
var request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.sandbox.tryspare.ae/data/sync/" + jobId))
    .header("Authorization", "Bearer " + accessToken)
    .header("x-tenant", "UAE")
    .GET()
    .build();
using var request = new HttpRequestMessage(
    HttpMethod.Get,
    $"https://api.sandbox.tryspare.ae/data/sync/{jobId}");
request.Headers.Add("Authorization", $"Bearer {accessToken}");
request.Headers.Add("x-tenant", "UAE");
var response = await client.SendAsync(request);
req, _ := http.NewRequest(
  http.MethodGet,
  "https://api.sandbox.tryspare.ae/data/sync/"+jobId,
  nil,
)
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("x-tenant", "UAE")
resp, err := http.DefaultClient.Do(req)

See Sync.

Generate a risk report

A risk report turns synced account data into a scored assessment you can review in your app or share as a PDF. Use it when you need a structured view of a customer's financial profile after sync completes. The flow has four parts: create the report, wait until it is ready, read the results, and optionally download a PDF copy.

3.1 Create a risk report

Ask Spare to build a report for an authorized consent. You receive a reportId to track progress.

curl -X POST https://api.sandbox.tryspare.ae/report/risk-reports \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "x-tenant: UAE" \
  -H "Content-Type: application/json" \
  -d '{ "consentId": "YOUR_CONSENT_ID" }'
const createResponse = await fetch("https://api.sandbox.tryspare.ae/report/risk-reports", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${accessToken}`,
    "x-tenant": "UAE",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ consentId }),
});
const { data: reportJob } = await createResponse.json();
const reportId = reportJob.reportId as string;
response = requests.post(
    "https://api.sandbox.tryspare.ae/report/risk-reports",
    headers={"Authorization": f"Bearer {access_token}", "x-tenant": "UAE"},
    json={"consentId": consent_id},
)
response.raise_for_status()
report_id = response.json()["data"]["reportId"]
var body = String.format("{\"consentId\":\"%s\"}", consentId);
var request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.sandbox.tryspare.ae/report/risk-reports"))
    .header("Authorization", "Bearer " + accessToken)
    .header("x-tenant", "UAE")
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString(body))
    .build();
var payload = new { consentId };
using var request = new HttpRequestMessage(HttpMethod.Post,
    "https://api.sandbox.tryspare.ae/report/risk-reports");
request.Headers.Add("Authorization", $"Bearer {accessToken}");
request.Headers.Add("x-tenant", "UAE");
request.Content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
body := strings.NewReader(fmt.Sprintf(`{"consentId":"%s"}`, consentId))
req, _ := http.NewRequest(http.MethodPost, "https://api.sandbox.tryspare.ae/report/risk-reports", body)
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("x-tenant", "UAE")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)

3.2 Poll the risk report status

Report generation runs asynchronously. Poll this endpoint until the status shows the report is ready, or until it fails so you can retry.

curl -X GET "https://api.sandbox.tryspare.ae/report/risk-reports/YOUR_REPORT_ID/status" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "x-tenant: UAE"
const statusResponse = await fetch(
  `https://api.sandbox.tryspare.ae/report/risk-reports/${reportId}/status`,
  {
    headers: {
      Authorization: `Bearer ${accessToken}`,
      "x-tenant": "UAE",
    },
  },
);
const { data: reportStatus } = await statusResponse.json();
response = requests.get(
    f"https://api.sandbox.tryspare.ae/report/risk-reports/{report_id}/status",
    headers={"Authorization": f"Bearer {access_token}", "x-tenant": "UAE"},
)
response.raise_for_status()
report_status = response.json()["data"]
var request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.sandbox.tryspare.ae/report/risk-reports/" + reportId + "/status"))
    .header("Authorization", "Bearer " + accessToken)
    .header("x-tenant", "UAE")
    .GET()
    .build();
using var request = new HttpRequestMessage(
    HttpMethod.Get,
    $"https://api.sandbox.tryspare.ae/report/risk-reports/{reportId}/status");
request.Headers.Add("Authorization", $"Bearer {accessToken}");
request.Headers.Add("x-tenant", "UAE");
var response = await client.SendAsync(request);
req, _ := http.NewRequest(
  http.MethodGet,
  "https://api.sandbox.tryspare.ae/report/risk-reports/"+reportId+"/status",
  nil,
)
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("x-tenant", "UAE")
resp, err := http.DefaultClient.Do(req)

3.3 Get a risk report

When the report is ready, fetch the full scored result (affordability signals, account summaries, and related fields) as JSON for display in your application.

curl -X GET "https://api.sandbox.tryspare.ae/report/risk-reports/YOUR_REPORT_ID" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "x-tenant: UAE"
const reportResponse = await fetch(
  `https://api.sandbox.tryspare.ae/report/risk-reports/${reportId}`,
  {
    headers: {
      Authorization: `Bearer ${accessToken}`,
      "x-tenant": "UAE",
    },
  },
);
const { data: report } = await reportResponse.json();
response = requests.get(
    f"https://api.sandbox.tryspare.ae/report/risk-reports/{report_id}",
    headers={"Authorization": f"Bearer {access_token}", "x-tenant": "UAE"},
)
response.raise_for_status()
report = response.json()["data"]
var request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.sandbox.tryspare.ae/report/risk-reports/" + reportId))
    .header("Authorization", "Bearer " + accessToken)
    .header("x-tenant", "UAE")
    .GET()
    .build();
using var request = new HttpRequestMessage(
    HttpMethod.Get,
    $"https://api.sandbox.tryspare.ae/report/risk-reports/{reportId}");
request.Headers.Add("Authorization", $"Bearer {accessToken}");
request.Headers.Add("x-tenant", "UAE");
var response = await client.SendAsync(request);
req, _ := http.NewRequest(
  http.MethodGet,
  "https://api.sandbox.tryspare.ae/report/risk-reports/"+reportId,
  nil,
)
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("x-tenant", "UAE")
resp, err := http.DefaultClient.Do(req)

3.4 Export a risk report to PDF

Download a PDF copy of the report for archival, email, or offline review. The response is a file attachment, not JSON.

curl -X GET "https://api.sandbox.tryspare.ae/report/risk-reports/YOUR_REPORT_ID/export" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "x-tenant: UAE" \
  -o risk-report.pdf
const pdfResponse = await fetch(
  `https://api.sandbox.tryspare.ae/report/risk-reports/${reportId}/export`,
  {
    headers: {
      Authorization: `Bearer ${accessToken}`,
      "x-tenant": "UAE",
    },
  },
);
const pdfBuffer = await pdfResponse.arrayBuffer();
response = requests.get(
    f"https://api.sandbox.tryspare.ae/report/risk-reports/{report_id}/export",
    headers={"Authorization": f"Bearer {access_token}", "x-tenant": "UAE"},
)
response.raise_for_status()
with open("risk-report.pdf", "wb") as pdf_file:
    pdf_file.write(response.content)
var request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.sandbox.tryspare.ae/report/risk-reports/" + reportId + "/export"))
    .header("Authorization", "Bearer " + accessToken)
    .header("x-tenant", "UAE")
    .GET()
    .build();
var response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofByteArray());
Files.write(Path.of("risk-report.pdf"), response.body());
using var request = new HttpRequestMessage(
    HttpMethod.Get,
    $"https://api.sandbox.tryspare.ae/report/risk-reports/{reportId}/export");
request.Headers.Add("Authorization", $"Bearer {accessToken}");
request.Headers.Add("x-tenant", "UAE");
var response = await client.SendAsync(request);
await File.WriteAllBytesAsync("risk-report.pdf", await response.Content.ReadAsByteArrayAsync());
req, _ := http.NewRequest(
  http.MethodGet,
  "https://api.sandbox.tryspare.ae/report/risk-reports/"+reportId+"/export",
  nil,
)
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("x-tenant", "UAE")
resp, err := http.DefaultClient.Do(req)
// read resp.Body and write to risk-report.pdf

See Risk Reports and the Risk Report guide page.

Key takeaways

  • The UAE AIS flow is request β†’ consent β†’ authorize β†’ sync β†’ poll sync status β†’ risk report.
  • Every call requires Authorization: Bearer and x-tenant: UAE.
  • Account Information endpoints integrate over REST today; official SDK coverage for AIS is planned after the SDK review completes.

On this page