Surfboard Payments

Developers

Pull Surfboard notices and product updates into your own systems to pass on to your merchants.

Pass Surfboard messages on to your merchants

The Messages API gives your systems the notices and product updates Surfboard shares with partners, so you can show them in your own back office or forward them to your merchants through your own channels. It's the same content as your Messages inbox, limited to what Surfboard marks as shareable.

Not for live incidents. Messages are announcements and operational notices that can wait a few minutes. Outages and degraded service (platform, API, transactions, settlements) are posted in real time on status.surfboardpayments.com. Subscribe there for instant incident alerts.

  • Read-only. Keys can only fetch messages; they can't change anything.
  • Server-to-server. Keep the key on your server. Never put it in a browser or app.
  • Filter before forwarding. Each message says which countries and currencies it concerns; send it only to merchants it applies to.

API keys

Create one key per system, for example “Back office” and “Staging”. Up to 5 active keys.

List messages

GEThttps://partner.surfboardpayments.com/api/v1/messages

Returns live messages shared with your partner account, oldest first. Authenticate with Authorization: Bearer <key>. Expired and withdrawn messages drop out of the response.

Query parameters

since optional
ISO 8601 timestamp. Only messages published after it. Pass the previous response's nextSince. Leave it out to get everything currently live.
limit optional
1–100, default 50. When hasMore is true, call again with the new nextSince.

cURL

curl https://partner.surfboardpayments.com/api/v1/messages?since=2026-09-01T00:00:00Z \
  -H "Authorization: Bearer $SURFBOARD_MESSAGES_KEY"

Node.js: incremental sync

// Run every 5 minutes; persist nextSince between runs.
let since = await loadCursor(); // null on the first run
let more = true;

while (more) {
  const url = new URL("https://partner.surfboardpayments.com/api/v1/messages");
  if (since) url.searchParams.set("since", since);

  const res = await fetch(url, {
    headers: { Authorization: `Bearer ${process.env.SURFBOARD_MESSAGES_KEY}` },
  });
  if (!res.ok) throw new Error(`Messages API ${res.status}`);
  const page = await res.json();

  for (const message of page.messages) {
    // Upsert by id: a message may be delivered more than once.
    await saveMessage(message);
  }
  since = page.nextSince;
  await saveCursor(since);
  more = page.hasMore;
}

Response

{
  "messages": [
    {
      "id": "3f2c8a51-7b0e-4d8a-9a3e-2f1c6b7d9e40",
      "kind": "update",
      "severity": "info",
      "publishedAt": "2026-09-24T09:00:00.000Z",
      "updatedAt": "2026-09-24T09:00:00.000Z",
      "expiresAt": null,
      "title": "SurfMini is here",
      "summary": "Our smallest terminal yet, shipping now.",
      "bodyMarkdown": "SurfMini is **available to order today**. …",
      "bodyHtml": "<p>SurfMini is <strong>available to order today</strong>. …</p>",
      "bodyText": "SurfMini is available to order today. …",
      "media": [
        {
          "type": "video",
          "url": "https://content.surfboardpayments.com/hubfs/videos/surfmini-launch-opt.mp4",
          "caption": "SurfMini launch",
          "poster": null
        }
      ],
      "link": { "url": "https://www.surfboardpayments.com/surfmini", "label": "Read more" },
      "appliesTo": {
        "audience": "merchants",
        "countries": ["SE", "NO"],
        "currencies": []
      }
    }
  ],
  "hasMore": false,
  "nextSince": "2026-09-24T09:00:00.000Z"
}

Message fields

idstring
Stable message id. Use it to de-duplicate and upsert.
kind"notice" | "update"
Operational notice (settlements, incidents, changes) or product update (launches, new features).
severity"info" | "warning" | "critical"
How urgent a notice is. Product updates are always info.
publishedAtISO 8601
When the message went live. The since cursor compares against this.
updatedAtISO 8601
Last edit. Messages can be corrected after publishing.
expiresAtISO 8601 | null
Hide the message after this time. Null means no expiry.
titlestring
Headline, plain text.
summarystring | null
One-line teaser, plain text. Good for lists and notifications.
bodyMarkdownstring
The full body as written: paragraphs, bold, italics, lists, links.
bodyHtmlstring
The body as HTML, already escaped and limited to p, strong, em, code, a, ul/li and h3–h5. Safe to embed.
bodyTextstring
The body as plain text, links written as “label (url)”.
mediaarray
Images and videos in display order; the first is the hero. Each has type ("image" | "video"), https url, caption and, for videos, an optional poster image.
linkobject | null
Primary call to action: url and label.
appliesTo.audience"merchants" | "partners" | "all"
merchants: meant for your merchants, pass it on. partners: for you as a partner, not for forwarding. all: both.
appliesTo.countriesstring[]
ISO 3166-1 alpha-2 countries the message concerns. Empty means every country.
appliesTo.currenciesstring[]
ISO 4217 currencies the message concerns. Empty means every currency.

Errors

Errors return JSON with a message.

400
Invalid since or limit parameter.
401
Missing, malformed or revoked API key.
429
Rate limited: more than 10 requests in a minute on one key. Wait the number of seconds in the Retry-After header.
503
The API is temporarily unavailable. Retry later.

Good practice

  • Poll every 5 minutes with since. Polling more often doesn't get you incidents sooner; those are on the status page. Each key allows 10 requests a minute, enough to page through a backlog in one sync.
  • Always send since on routine polls. Without it you re-download every live message each time.
  • Upsert by id. A message can appear again, and may have been edited (updatedAt).
  • To mirror withdrawals, periodically fetch every page without since and hide messages no longer returned.
  • Media URLs are public, cacheable https links on Surfboard's content domain; embed them directly.
  • Revoke a key here straight away if it may have leaked, then create a new one.