SpareSpare Docs
GuidesAPI Reference

Link Web SDK

Add Spare Link to a website with React or plain JavaScript. Payments, mandates and account information for the UAE.

The Link Web SDK (@spare-technologies/spare-link-web) shows Spare Link on your website. Your backend creates a payment request or an account request. The SDK shows bank selection and consent to the payer, then sends the payer to their bank in the same tab. Your backend confirms the result. The SDK makes no API calls and holds no credentials.

Link Web SDK

npm install @spare-technologies/spare-link-web

Available now

This SDK is published on npm (1.0.0). Pin an exact version in package.json, and read the release notes before you upgrade. It serves the UAE only.

  1. Your backend creates the request and passes its reference to your page.
  2. Your page opens Spare Link with the reference.
  3. The payer selects a bank and gives consent in Spare Link.
  4. The SDK shows a loader and sends the whole tab to the bank. The bank never loads inside a frame.
  5. The payer's bank returns the payer to your return URL.
  6. Your backend reads the result from Spare.

The browser gets no onSuccess callback. Your page unloads when the payer goes to the bank, so your backend reads the result.

WayThe payer seesHow
ModalA dialog or a side drawer over your page, open until the bankopen()
InlineSpare Link inside your page, at the width and height you choosesession.mountIframe(element)
RedirectThe whole tab goes to the Spare page, then to the banksession.redirectToHosted()

Modal and inline show Spare Link inside your page, so that page must be one of the API key's trusted origins. Where your page sets a Content Security Policy, allow the Spare API host in frame-src.

Prerequisites

  • An API key for your backend, with your website under Trusted origins. Read Register Your Website.
  • A backend that calls the Spare API with Authorization: Bearer and x-tenant: UAE. Read Integration flow.
  • Signed payment requests: POST /payment-requests needs an x-signature header. Read Request signing.
  • Return pages on https for successRedirectUrl and failureRedirectUrl.
  • React 18 or 19, only if you use the React entry (@spare-technologies/spare-link-web/react).

Register Your Website

Spare shows Spare Link inside a page only when that page is a trusted origin of the API key that created the request. This stops another website from framing your payment page. Each API key holds its own list.

  1. In the Spare dashboard, go to Developer, then API keys.
  2. Open the API key your backend uses to create requests, or create one.
  3. Under Trusted origins, add each website that shows Spare Link. For example, add your production site and your staging site.
  4. Save the key.

An origin is a scheme, a host, and an optional port, with no path. A key holds up to 10.

ValidNot valid
https://shop.example.comhttps://shop.example.com/checkout (has a path)
https://shop.example.com:8443http://shop.example.com (not https)
https://staging.example.comhttps://*.example.com (wildcards are not allowed)
https://xn--mnchen-3ya.dehttps://mΓΌnchen.de (write a name with accents in its punycode form)
https://shop.example.com:443 (leave the default port out)
https://localhost or https://192.168.1.5 (the host must be a public name)

Two failures follow from a missing origin:

  • A payment request created with channel: 'sdk_web' fails when its API key has no trusted origins, even if you only redirect. The error message names the setting to add.
  • Where the page that shows Spare Link is not on the list, the browser refuses the frame and the SDK reports BRIDGE_TIMEOUT. This applies to payments and account information.

Add the origin of a new website before you show Spare Link on it. Requests already created with that key use the updated list immediately.

Create the Request on Your Backend

Your backend creates the request with the Spare API or the TypeScript SDK, using the API key that holds your website under Trusted origins. The Link Web SDK needs these fields on the request:

FieldRequestValue
channelPayment request onlysdk_web
successRedirectUrlPayment and account requestAn https page on your website
failureRedirectUrlPayment and account requestAn https page on your website

An account request has no channel field.

Payments

Create the payment with POST /payment-requests. Make Your First Payment shows the full request, and Setup Your First Mandate shows a mandate. The response returns internalReference. Pass it to your page as the reference.

The SDK supports every payment type. Only type and instructions change between them.

typeThe payer approvesGuide
SingleInstantPaymentOne payment now, domestic or internationalSingle payment
FixedDefinedScheduleFixed amounts on set datesDefined schedule
VariableDefinedScheduleSet dates, each with a maximum amountDefined schedule
FixedPeriodicScheduleThe same amount every periodRecurring
VariablePeriodicScheduleEvery period, up to a maximum amountRecurring
FixedOnDemandA fixed amount, taken when you askOn demand
VariableOnDemandAny amount up to a limit, taken when you askOn demand

The payer approves a mandate once. Your backend then takes each payment with POST /mandate/schedule. Read Manage your mandates. An international payment is a SingleInstantPayment with isInternationalPayment: true. Read International payments.

Account Information

Create the account request with POST /data/account-requests. In the UAE, the request also requires referenceCustomerId and customerEmail. Read Account Requests for the full request.

The response returns an id and a requestLink. The reference is the last segment of the requestLink path. Keep the id: your backend reads the result with it.

Open Spare Link with the reference from your backend and the environment you are testing in. This example saves the order before the tab goes to the bank and logs a cancel or an error.

import { useSpareLink } from "@spare-technologies/spare-link-web/react";

export function PayButton({ reference }: { reference: string }) {
  const { open, ready } = useSpareLink({
    reference, // from your backend
    environment: "sandbox", // or "production"
    onOpenUrl: () => sessionStorage.setItem("pendingOrder", "order-1042"),
    onExit: (reason) => console.log("closed", reason), // "cancel" or "user_closed"
    onError: (error) => console.error(error.code, error.message),
  });

  return (
    <button disabled={!ready} onClick={() => open()}>
      Pay with your bank
    </button>
  );
}

For account information, add product: "data". The React entry also has a ready-made button:

import { SpareLinkButton } from "@spare-technologies/spare-link-web/react";

<SpareLinkButton reference={reference} environment="sandbox">
  Pay with your bank
</SpareLinkButton>;
import { SpareLink } from "@spare-technologies/spare-link-web";

const link = SpareLink.payment.create({
  reference, // from your backend
  environment: "sandbox", // or "production"
  onOpenUrl: () => sessionStorage.setItem("pendingOrder", "order-1042"),
  onExit: (reason) => console.log("closed", reason),
  onError: (error) => console.error(error.code, error.message),
});

payButton.onclick = () => link.open();

For account information, use SpareLink.data.create(...) with the same options.

Customize the Modal

The options of open() change how the modal looks. They never change what the payer approves. This example opens a drawer on the right with your logo:

open({
  position: "right",
  width: "32rem",
  logoUrl: "https://shop.example.com/logo.png",
});
OptionDefaultDescription
titlePer productThe heading above Spare Link.
logoUrl-An https image for the header. The SDK ignores any other URL.
position"center""center" is a dialog. "right" is a full-height drawer.
width"30rem"Any CSS length, such as 30rem, 90vw or min(30rem, 100%). Use rem so the width follows the payer's text size. The modal is never wider than the screen.
height"45rem"Any CSS length. A drawer is full height unless you set one.
fullscreenOnMobiletrueFills the screen when the viewport is 30rem wide or less.
zIndex2147483000The stacking order, for pages with their own layers.
dismissibletrueCloses on Escape and on a click outside. The close button always works.
onDismiss-Runs when the payer closes the modal before the bank.

Size an Inline Frame

Pass the size to mountIframe. Without a size, the frame fills its element.

session.mountIframe(element, { width: "26rem", height: "40rem" });
OptionDefaultDescription
width100%Any CSS length, such as 26rem. The frame is never wider than its element.
height100%Any CSS length. Set it when your element has no height of its own, or the frame has none either.

Confirm the Result

When the payer returns, your return page loads with ?status=success or ?status=failure. Treat it as a hint only, because anyone can open that URL. Your backend must confirm the status with Spare before you fulfil anything.

RequestRead withSuccessFailure
Single paymentGET /payment-requests/{reference}ProcessedRejected, Errored
Mandate (any schedule type)GET /payment-requests/{reference}ScheduledRejected, Errored
Account informationGET /data/account-requests/{id}ApprovedRejected, Expired

Webhooks reach your backend sooner than polling. Read Webhooks.

Read the Account Data

An approved account request gives a consent. The account data is available after a sync:

  1. Call POST /data/sync?consentId={consentId} with { "syncAccounts": true, "syncBalances": true, "syncTransactions": true }. The response returns a jobId.
  2. Call GET /data/sync/{jobId} until account, balance and transaction are Succeeded.
  3. Call GET /data/accounts?consentId={consentId}.

Read Sync and Accounts.

Environments

The environment option picks the Spare host, so you never copy a URL.

environmentThe reference must come fromReal money
"sandbox"The Spare sandboxNo
"production"Spare productionYes

The reference must come from the same environment as the option. In the sandbox, the console says so once and the modal header shows a Sandbox badge. A baseUrl on a Spare domain must be one of Spare's own hosts. The SDK refuses any other Spare host.

Go to the Bank

When the payer chooses a bank, Spare Link asks the SDK to open it. The SDK covers Spare Link with a loader and sends this tab to the bank. The loader stays until the bank page loads, so the payer never sees a blank page.

  • To change the loader text, set handoffText.
  • To show your own loader, set handoffLoader: false and use onHandoff.
  • To keep the tab on your page, set openUrlStrategy: "none". The SDK then only passes the URL to onOpenUrl.

On a Phone

On a phone, the payer's bank may open its own app instead of its website. The SDK sends the tab to the bank link in both cases. Where the payer returns to your tab without finishing, the SDK removes the loader. Where the payer finishes, the bank returns them to your return URL. Test this flow on a real phone with each bank before you go live.

The consent page links to Spare's Terms of Service and Privacy Policy. When the payer taps one, the SDK opens it in a new tab. This tab, the frame and the modal stay on the consent page, so the payer can come back and pay or cancel. The behaviour is the same for every openUrlStrategy, and it never sends the payer to the bank.

The SDK reports the tap through onEvent. Your page does not need to handle it:

onEvent: (name, data) => {
  if (name === "open_document") console.log(data.url, data.opened);
},
FieldDescription
urlThe document address. It is always https.
openedfalse when the browser blocked the new tab.

Open Finance rules ask Spare to give the payer's bank context about each payment, so the bank can judge its risk. Spare Link collects this context in the payer's browser, inside Spare's frame. Your page does not collect it, cannot read it, and cannot supply it.

  • Device and browser facts. Device type, operating system, screen, connection type, browser details and how the payer scrolled. These need no prompt.
  • Location. Spare Link asks the payer once, on the consent screen. Where the payer declines, the payment goes ahead without it. The SDK gives the frame allow="payment; geolocation", so the browser's prompt names your website.

Two things fall to you:

  • Do not block location on your page. A Permissions-Policy header with geolocation=() stops the prompt. Leave it out, or allow Spare's hosts, for example geolocation=(self "https://api.tryspare.ae" "https://api.sandbox.tryspare.ae").
  • State it in your privacy policy. Spare Link collects location and device details during payment on your page.

Options

useSpareLink, SpareLink.payment.create and SpareLink.data.create take the same options.

OptionDefaultDescription
referenceRequiredFrom your backend.
environmentRequired"sandbox" or "production". Read Environments.
tenant"UAE"The Spare market.
product"payment""data" for account information. React only. JavaScript uses SpareLink.data.
modal-Default modal options. React only.
handoffLoadertrueShows a loader from the moment the payer leaves until the bank loads. Read Go to the Bank.
handoffTextTaking you to your bank…The text of that loader.
openUrlStrategy"top""top" sends this tab to the bank. "none" only passes the URL to onOpenUrl.
allowedMessageOriginsThe Spare API originExact origins only. The SDK rejects *, paths and plain http.
readyTimeoutMs60000Reports BRIDGE_TIMEOUT when Spare Link does not start in time.
debugfalseLogs the lifecycle to the browser console.
baseUrl-Advanced. Replaces environment and tenant. https only, or http on localhost.

useSpareLink returns open(options?), close(), ready, error and session. ready is true once Spare Link can open. error is set when an option is not valid.

Callbacks

CallbackWhen it runs
onReadySpare Link is ready.
onOpenUrl(url)Spare Link sends the payer to the bank. Save your order reference here.
onHandoff(url)This tab is about to go to the bank. It runs once, after onOpenUrl, and only when the SDK navigates.
onExit(reason)The payer cancelled ("cancel") or closed the modal ("user_closed").
onError(error)Something failed. Read Error Codes.
onEvent(name, data)Every event, by name: ready, open_url, open_document, handoff, exit, error.

Error Codes

onError receives an error with a code and a message.

CodeMeaningWhat to do
INVALID_CONFIGAn option is not valid, for example no reference or environment, or a width with no unit.Read the message.
BRIDGE_TIMEOUTSpare Link did not start in time.Check that the reference comes from the same environment and that your website is under Trusted origins. For a payment, check that it has channel: 'sdk_web'.
IFRAME_LOAD_FAILEDThe frame did not load.Check the network and your Content Security Policy.
HOSTED_ERRORSpare Link reported an error. The message is the Spare code.For request_expired, create a new request. Read When a Request Is Already Over.
DESTROYEDA session was used after destroy().Create a new session.

When a Request Is Already Over

Where the payer opens a request that has already ended, the frame shows the reason and your page receives one of these:

Spare codeWhat it meansWhat your page gets
request_expiredThe request ran past its expiry.onError with HOSTED_ERROR
request_processedThe payment is already complete.onError with HOSTED_ERROR
request_errored, something_went_wrongThe request failed on Spare's side.onError with HOSTED_ERROR
request_rejectedThe request was cancelled.onExit("cancel")

The frame shows no return link to your website, so decide the next step in onError or onExit, then confirm the result on your backend. A reference that Spare does not know has no request to show, so the SDK reports BRIDGE_TIMEOUT.

On this page