Skip to content
Language: English

API for developers

Send sales and your own data to LinkHarbor

Updated 1 November 2026 5 min read

Use sale callbacks to match sales from your shop to link clicks, and “Custom (API)” to send live data from your own tools to your page.

LinkHarbor also accepts data from outside. For two cases there are dedicated endpoints with their own secrets – you do not need an API key for them: sale callbacks report sales from your shop, so you can see in your statistics which link brought in revenue. The “Custom (API)” integration brings live data from your own tools onto your page as a widget and can trigger push notifications.

Sale callbacks: matching sales

How the click ID (lt_click_id), matching and reporting fit together is explained in Attributing conversions and sales; here you will find all the fields and responses for developers. In short: when LinkHarbor redirects a counted click to an http or https target, it appends the parameter lt_click_id; your shop remembers it and sends it back after the purchase.

Create a callback secret

The “Callback & API” page with the “Callback secrets” card
You create callback secrets on the “Callback & API” page
  1. Open “Callback & API”
  2. In the “Callback secrets” card, click “+ Create new secret”
  3. Optionally give it a name, for example “Shop callback”, and click “Create secret”
  4. Copy the secret straight away – it is only shown once
Who
Owners and admins, from Creator Pro. In Creator Free it says “Callback secrets are available from Creator Pro.”
How many
Up to 5 secrets per workspace, for example one per shop.
“Edit”
Change the name and set the status to “Active” or “Inactive”. With an inactive secret, the API rejects callbacks.
“↺ Rotate”
Generates a new secret; the old one stops working immediately.
“Last callback:”
Shows when the secret was last used successfully.

Send a callback

POST /v1/callbacks/sale
curl -X POST https://dock.at-harbor.com/v1/callbacks/sale \
  -H "Content-Type: application/json" \
  -H "X-Callback-Secret: $LH_CALLBACK_SECRET" \
  -d '{
    "click_id": "3f1c2a9e-8d4b-4a51-9f0e-6b7c1d2e3f40",
    "order_id": "A-10023",
    "amount": "49.90",
    "currency": "EUR",
    "occurred_at": "2026-11-01T10:15:00Z"
  }'
# → {"ok":true,"conversion_id":123}
FieldRequiredMeaning
click_idYesThe value of lt_click_id (10–36 characters)
order_idYesYour order number (1–128 characters); each order number is only counted once per workspace
amountYesAmount as a decimal number, for example "49.90"; no negative values
currencyYesCurrency as a three-letter code, for example EUR; the API converts lowercase letters
occurred_atNoTime of purchase (ISO 8601)
amount_base_minor, base_currency, fx_rateNoConverted amount in the smallest unit, base currency and exchange rate, if you sell in several currencies
metaNoYour own extra data as a flat object: up to 50 keys with 1–64 characters; values are text (up to 500 characters), a number, true/false or null; nested objects and lists are rejected
workspace_idNoYour workspace ID; with it, the API reports an unknown click_id as 404 instead of 403
ResponseMeaning
200 {"ok":true,"conversion_id":…}Sale saved – or this order_id was already saved (then the ID of the existing conversion)
403 forbiddenWrong secret or signature, or unknown click_id (without workspace_id)
403 secret_inactiveThe secret is set to “Inactive”
404 not_foundUnknown click_id (only with workspace_id and a valid secret)
413 request_too_largeThe body is larger than 1 MB
422 validation_failedA field is missing or invalid, for example a value in meta that is too long or nested
429 rate_limitedMore than 120 callbacks per minute from your IP address

Optional signature

In addition to the secret, you can send the X-Callback-Signature header. It contains sha256= and the HMAC-SHA256 signature of the raw body in hex. The key is not the secret itself but its SHA-256 hash in binary form. If you send the header, it must be correct – otherwise the callback is rejected.

Calculate the signature (PHP)
<?php
$body = json_encode($payload);
$key  = hash('sha256', getenv('LH_CALLBACK_SECRET'), true); // binary hash of the secret
$sig  = 'sha256=' . hash_hmac('sha256', $body, $key);
// Headers: X-Callback-Secret and X-Callback-Signature: $sig, body: $body

Where you see the sales

Reported sales appear as conversions in your statistics, see Attributing conversions and sales. Via the API, you read them with read:stats under /v1/stats/…/conversions/breakdown.

Custom (API): your own live data

With the “Custom (API)” integration, any program that can send HTTP requests – a game server plugin, a bot, a cron script – can send data to LinkHarbor. It turns into a live widget on your page, and push rules can notify your followers.

Connect

  1. Open “Integrations” in the workspace
  2. Choose “Custom (API)” and enter a name
  3. Once connected, the dialog shows the “Ingest URL” and the “Secret” (with “Show secret”)
  4. Store both in your tool

Integrations are available from Creator Pro. Only owners and admins can see the ingest URL and secret, including later in the integration's detail view. Disconnecting and reconnecting creates a new secret. How to connect integrations in general is explained in Connect an integration.

Send data

Send an event and the widget state
curl -X POST "$INGEST_URL" \
  -H "Authorization: Bearer $INGEST_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "event": "player.joined",
    "event_id": "join-2026-11-01T20:01:02Z-steve",
    "data": { "player": "Steve" },
    "state": {
      "type": "game_server",
      "name": "Survival SMP",
      "online": true,
      "players_online": 3,
      "players_max": 20
    }
  }'
# → {"ok":true,"event_id":4711,"duplicate":false,"event_type":"player.joined"}
event
Free-form event name, for example player.joined. It is converted to lowercase and is the name push rules react to. Without event, the event is called state.updated.
event_id
Optional key that prevents double processing. If the same event_id is sent again, LinkHarbor responds with duplicate: true and does nothing.
data
Values for the placeholders in push texts, for example {player}. Up to 40 keys, simple values only.
state
The complete state of the widget. It fully replaces the previous one. type is game_server (game server card) or status (status card).

Instead of Authorization: Bearer, you can also send the secret in the X-LinkHarbor-Secret header. 120 calls per minute are allowed per IP address. Optionally, you sign the body: the X-LinkHarbor-Signature header contains sha256= and the HMAC-SHA256 signature of the raw body with the secret as the key. If you send it, it must be correct.

ResponseMeaning
200 {"ok":true,…}Event saved, with duplicate: true for a known event_id
400 invalid_jsonThe body is not a JSON object
401 invalid_secretWrong secret or unknown ingest URL
401 signature_invalidX-LinkHarbor-Signature does not match the body
413 request_too_largeThe body is larger than 64 KB
422 validation_failedNeither event nor state in the body
429 rate_limitedMore than 120 calls per minute from your IP address

Nothing found?

Tell us what you need, and our support team will help you personally.

Contact support