Connecting a shop

A tenant admin approves a shop on our consent page. The shop's app then exchanges a single-use link token for its credentials, server to server. No secret passes through a browser URL.

1. Send the admin to the consent page

Open this page top-level (it can't be framed). The admin signs in to Ábyrg Verslun if needed and approves the shop.

https://<our-host>/en/integrations/shopify/connect?shop=<shop>.myshopify.com&state=<state>

shop is a lowercase .myshopify.com host. state is a random value you generate and keep (at most 512 characters). A shop that is already connected can't be connected again until the first connection is revoked.

2. Check the redirect

On approval we redirect to the connect redirect URL set for your app (SHOPIFY_APP_CONNECT_REDIRECT_URL, always https) with two query parameters: link_token and state. The link token is valid for five minutes and works once.

Before you redeem, your app must check two things. That state equals the value it generated for this browser session, and that the shop it is about to connect is the shop that session belongs to. Without them an attacker can make your app connect their link token to someone else's shop, or the other way round.

3. Redeem the link token

Call this from your backend, never a browser. It is protected by the app secret you share with us (SHOPIFY_APP_SECRET) and by the link token.

curl -X POST https://<our-host>/api/v1/integrations/shopify/complete-link \
  -H "Authorization: Bearer $APP_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"link_token": "<link-token>", "shop": "<shop>.myshopify.com"}'

Answer, 200 with Cache-Control: no-store:

{
  "client_id": "<client-id>",
  "client_secret": "<client-secret>",
  "hmac_key": "<hmac-key>",
  "token_endpoint": "https://<our-host>/api/oauth/token"
}

Store all three secrets on your backend straight away. We do not show them again: client_secret signs you in, hmac_key verifies the result callbacks.

StatusBodyMeaning
400{"error":"invalid_or_expired"}Unknown, used, expired or another shop's link token; or a malformed body. All look the same.
401{"error":"unauthorized"}Missing or wrong app secret.
429{"error":"rate_limited"}Too many attempts; wait Retry-After seconds.
500 / 503{"error":"server_error"}The server is not configured or could not check the limit. Nothing was redeemed.

4. Get an access token

Sign in with the client credentials, as in Authentication. The client holds the scopes transactions:write and transactions:read, nothing else.

curl -X POST https://<our-host>/api/oauth/token \
  -d grant_type=client_credentials \
  -d client_id=<client-id> \
  -d client_secret=<client-secret>

Next: starting a Verification. Problems: Errors.