Approve users
Harbor Approve is the consent screen your users see once. It sets their limits, connects their stores, and gives you a user token.
Approve works like Plaid Link. Your server creates an approval token, the user approves in Harbor's hosted screen, you get back a short-lived public token, and your server exchanges it for a long-lived user token.
POST /v1/approval_tokensuser_token you keep.Create an approval token
curl https://www.useharbor.io/v1/approval_tokens \
-H "Authorization: Bearer $HARBOR_KEY" \
-H "content-type: application/json" \
-d '{
"client_user_id": "user_123",
"rules": { "max_per_order": 15000, "max_per_day": 30000, "confirm_above": 7500 },
"merchant_ids": ["target", "allbirds"],
"redirect_uri": "https://yourapp.com/harbor/done"
}'| Field | Required | What it does |
|---|---|---|
client_user_id |
yes | Your id for the user. Approving the same id again updates the same Harbor user. |
rules |
no | Suggested limits, in cents. The user sees them and can change them before approving. |
merchant_ids |
no | Stores to offer for connecting, from GET /v1/merchants. |
redirect_uri |
no | Where to send the user afterwards, with ?public_token=… added. |
The response has the approval_token, its expiration (30 minutes) and a hosted_url.
Spending rules
| Rule | Meaning |
|---|---|
max_per_order |
The most one order can cost, including shipping and tax. Over it, the checkout fails with RULE_VIOLATION. |
max_per_day |
The most all orders together can cost in 24 hours. Checked when you confirm. |
confirm_above |
At or above this total, the user confirms the order themselves (see confirm). |
allowed_merchants |
Only these merchant ids. |
currency |
Currency for the amounts. Defaults to USD. |
Harbor checks every rule against the store's real total, not the price you saw earlier.
Show Harbor Approve
Open hosted_url in a popup, a new tab or a webview. The user sees your app's name, what it can and can't do, their limits, and the stores they can connect.
In a web app: popup and message
const popup = window.open(approval.hosted_url, "harbor", "width=460,height=720");
window.addEventListener("message", async (event) => {
if (event.origin !== "https://www.useharbor.io") return;
if (event.data.type === "harbor:success") {
// Send it to your server, which exchanges it.
await fetch("/api/harbor/exchange", { method: "POST", body: JSON.stringify({ public_token: event.data.public_token }) });
}
if (event.data.type === "harbor:exit") {
// The user canceled. Nothing was shared.
}
});Anywhere else: redirect
Pass redirect_uri when you create the token. After approving, the user lands on https://yourapp.com/harbor/done?public_token=public-live-….
Exchange the public token
Do this on your server. The public token works once and expires with the approval token.
curl https://www.useharbor.io/v1/approval_tokens/exchange \
-H "Authorization: Bearer $HARBOR_KEY" \
-H "content-type: application/json" \
-d '{ "public_token": "public-live-…" }'{ "user_token": "user-live-…", "user_id": "usr_4n4363bk6m0x516172" }Store the user_token with your user, encrypted like any credential. Send it as the Harbor-User-Token header on checkout, order and connection requests. It belongs to your key's environment: sandbox user tokens only work with sandbox keys.
Connecting major retailers
Target, Best Buy and The Home Depot run on the user's own device, using the store account they already have. In Approve, the user signs in on the store's own pages — never on Harbor, and no credentials touch Harbor's servers. On the desktop web this runs through the Harbor extension (paired in the same step); in your mobile app, the Harbor SDK will do it with nothing for the user to install. Until a store is connected, checkouts there fail with CONNECTION_NEEDS_USER; send the user through Approve again to connect it. See Stores and routes.
The Harbor extension is in early access. Email hello@useharbor.io to try it with your users.
In the sandbox
POST /v1/sandbox/public_tokenapproves without the screen. See the quickstart.POST /v1/sandbox/connectionsconnects a sandbox store for a user. See Sandbox.