AntivioesLog in

Building an app (OAuth)

An app is an integration many companies or merchants can install, each approving what it may do. Your own store connecting to your own account does not need one: use an API token.

Apps use the OAuth 2.1 authorization code flow with PKCE. There is no SDK to learn; every step is a plain HTTPS request.

1. Open a developer account and register an app

Sign in to the dashboard, accept the developer terms and register your app. You choose:

You get a client id (app_…). It is public.

The same is available over the API under /v1/developer/… for signed-in people (not tokens).

2. Send the person to approve

Open the user's browser at the web app's consent page, with a PKCE challenge and a state you check when they come back:

https://cloud.antivio.app/oauth/authorize
  ?response_type=code
  &client_id=app_Ab3xK9mQ2wL7pZ4nR8tY1vC5
  &redirect_uri=https%3A%2F%2Facme.example%2Foauth%2Fcallback
  &scope=shipments%3Aread%20shipments%3Awrite
  &state=<random string you keep>
  &code_challenge=<base64url(sha256(code_verifier))>
  &code_challenge_method=S256

Make a fresh code_verifier (43 to 128 characters of A-Z a-z 0-9 - . _ ~) for every attempt and keep it on your server (or in the session). state and PKCE are both required.

The person signs in, picks the company (or merchant account) and approves the scopes. Only people who can manage integrations there can install an app, and they can only give scopes their own role holds. Antivio then sends the browser to your redirect URI:

https://acme.example/oauth/callback?code=atc_…&state=<your state>

If they decline, the redirect carries error=access_denied and your state. Check state before you use the code. The code works once and for 60 seconds.

3. Exchange the code for tokens

curl -X POST https://api.antivio.app/oauth/token \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=authorization_code \
  -d code="$CODE" \
  -d redirect_uri="https://acme.example/oauth/callback" \
  -d code_verifier="$CODE_VERIFIER"

Confidential apps authenticate with HTTP Basic (above) or client_id and client_secret in the form body; public apps send only client_id.

{
  "access_token": "atk_live_…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "atr_…",
  "scope": "shipments:read shipments:write"
}

The access token is the same bearer as an API token: send it as Authorization: Bearer … to the public API. It acts as the person who approved, limited to the granted scopes, so it stops working if they leave the company. It lasts an hour.

4. Refresh

curl -X POST https://api.antivio.app/oauth/token -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=refresh_token -d refresh_token="$REFRESH_TOKEN"

Every refresh returns a new refresh token and spends the old one. Store the new one before you use the new access token. Refresh tokens last 30 days.

If a refresh token or a code is used twice, Antivio assumes it leaked and revokes every token of that installation; the person has to approve again. Do not retry a refresh in parallel.

5. Revoke

curl -X POST https://api.antivio.app/oauth/revoke -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d token="$REFRESH_TOKEN"

Revoking an access token ends only that token; revoking a refresh token ends all of the installation's tokens. The answer is 200 even for a token we do not know.

6. Receive webhooks

With webhooks:write, your access token adds webhook endpoints like any API token (POST /v1/webhook-endpoints). They belong to the installation:

7. App events and privacy requests

Set your app's events URL in the developer portal (or PUT /v1/developer/apps/{id}/events with {"url": "https://…"}). Antivio sends there, for every company that installed your app, messages signed with your app's events secret (whsec_…, shown in the portal) the same way as webhooks: verify webhook-signature with the same code, answer 2xx, use webhook-id to ignore repeats. Failed messages are retried for about three days.

TypeWhendata
app.uninstalledthe company removed your app; its tokens and webhook endpoints are goneinstallation_id, customer_account_id, uninstalled_at
customer.data_requesta recipient asked the company for the data held about themprivacy_request_id, installation_id, customer_account_id, recipient (email, phone), shipment_ids, requested_at
customer.redacta recipient asked to be erasedthe same as customer.data_request

8. Publish in the App Store

A private app is installed from a link you share, with no review. To let any company find and install it, submit it for review; once Antivio approves it, it is listed in the App Store inside every company's dashboard.

Before you submit, the app needs a privacy policy URL, a support URL and an events URL (it must receive app.uninstalled and privacy requests). Then:

curl -X POST https://api.antivio.app/v1/developer/apps/$APP_ID/submissions \
  -H "Authorization: Bearer $USER_TOKEN" -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" -d '{
    "listing": {
      "tagline": "Keep your store'"'"'s orders and shipments in sync",
      "description_en": "Sends every order to Antivio and brings the tracking back.",
      "description_es": "Envía cada pedido a Antivio y trae el seguimiento.",
      "category": "ecommerce",
      "app_url": "https://acme.example/install",
      "scope_justifications": [
        { "scope": "shipments:read", "justification": "Shows each order'"'"'s delivery status to the buyer." }
      ]
    },
    "note": "First version."
  }'

Errors

The token endpoint answers with RFC 6749 errors, not problem details:

errorStatusMeaning
invalid_request400A parameter is missing or malformed (code_verifier shape, form body)
invalid_client401Unknown, suspended or wrongly authenticated app
invalid_grant400The code or refresh token is unknown, used, expired or for another app or redirect URI; or the app was uninstalled
unsupported_grant_type400Only authorization_code and refresh_token exist. There is no password, implicit or client-credentials grant

Server metadata

GET /.well-known/oauth-authorization-server (RFC 8414) lists the endpoints, S256, and the supported scopes.

Staying safe

All guides