Quickstart
This guide takes you from partner credentials to a working integration: a seller connects your application, you receive tokens, you call the API, and you receive webhooks.
Before you start
Demarky registers your application and sends you:
| Item | Notes |
|---|---|
client_id | Public identifier, shaped app_… |
client_secret | Shown to Demarky staff once and sent to you once. Store it encrypted. |
| Granted scopes | The ceiling for every seller grant. Requesting anything outside it fails. |
| Registered redirect URIs | Matched exactly, including scheme, host, port, and path |
Send Demarky your redirect URI before you start: an unregistered value is rejected at the first step.
1. Send the seller to Demarky
Your application starts the flow. Generate a fresh state and a PKCE code_verifier per attempt, store both against the browser session, and redirect the seller's browser — this is a full-page navigation, not a background request.
https://api.demarky.ai/oauth/authorize
?client_id=app_0123456789abcdefghjkmnpqrs
&redirect_uri=https://partner.example/oauth/callback
&response_type=code
&scope=products:read%20pages:write
&state=ac9f7e2b13c84f0f1d7a
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256
| Requirement | Rule |
|---|---|
state | At least 16 characters of randomness; returned unchanged on success and on error |
code_challenge | Base64url SHA-256 of your verifier, 43–128 characters |
code_challenge_method | S256 only; plain PKCE is rejected |
scope | Space-separated, at least one, all within your registered scopes |
The seller signs in to Demarky, reviews the permissions, and approves. Their browser returns to your redirect_uri with code and state. If they decline, it returns with error=access_denied instead.
2. Exchange the code
Validate that state matches what you stored, then exchange the code from your server. The code is single-use and expires in five minutes.
curl -X POST https://api.demarky.ai/oauth/token \
-u "$DEMARKY_CLIENT_ID:$DEMARKY_CLIENT_SECRET" \
-d grant_type=authorization_code \
-d code=dmk_ac_live_xxx \
-d redirect_uri=https://partner.example/oauth/callback \
-d code_verifier=your_original_pkce_verifier
{
"access_token": "dmk_at_live_xxx",
"token_type": "Bearer",
"expires_in": 600,
"refresh_token": "dmk_rt_live_xxx",
"scope": "products:read pages:write",
"account_id": "acct_0123456789abcdefghjkmnpqrs",
"application_id": "app_0123456789abcdefghjkmnpqrs",
"installation_id": "ins_0123456789abcdefghjkmnpqrs"
}
3. Store the installation
| Field | Why you need it |
|---|---|
installation_id | Your primary key for this connection; stable across re-consent |
account_id | Goes in the path of every resource request |
access_token + expiry | Expires in 10 minutes |
refresh_token | Rotates on every use — always persist the newest one |
scope | What this installation may actually do |
There is no endpoint that returns these later. If you lose them, the seller has to reconnect.
4. Call the API
curl -H "Authorization: Bearer dmk_at_live_xxx" \
https://api.demarky.ai/v1/accounts/acct_0123456789abcdefghjkmnpqrs/products
A 401 with invalid_token means the token expired or the seller disconnected: refresh once and retry, and treat a second failure as disconnected. A 403 with insufficient_scope is permanent for this installation — retrying will not help, because only a new consent can widen a grant.
5. Keep the connection alive
curl -X POST https://api.demarky.ai/oauth/token \
-u "$DEMARKY_CLIENT_ID:$DEMARKY_CLIENT_SECRET" \
-d grant_type=refresh_token \
-d refresh_token=dmk_rt_live_xxx
Three rules decide whether your integration survives in production:
- Refresh one request at a time per installation. Refresh tokens rotate, and presenting a spent one is treated as theft: Demarky invalidates the entire installation. Two concurrent refreshes are enough to trigger it, so collapse them behind a per-installation lock.
- Persist the new refresh token before you use the new access token. If that write is lost, your next refresh presents a spent token and the installation is gone.
- Build a reconnect path. A refresh-token family is capped at 90 days from first issue, regardless of activity. Every seller passes through consent again at least quarterly. An
invalid_grantresponse is terminal: stop retrying and prompt the seller to reconnect.
6. Receive webhooks
Webhook endpoints belong to your application, not to individual sellers, and you manage them yourself with your client credentials. Deploy your receiver first — Demarky posts a signed challenge to the URL during registration, and an endpoint whose challenge fails stays in pending_verification.
curl -X POST https://api.demarky.ai/v1/developer/webhook-endpoints \
-u "$DEMARKY_CLIENT_ID:$DEMARKY_CLIENT_SECRET" \
-H 'Content-Type: application/json' \
-d '{"url":"https://partner.example/webhooks/demarky","events":["lead.created"]}'
The challenge arrives as a POST with header Demarky-Event-Type: endpoint.verification and a JSON body containing a challenge nonce. Answer 200 and echo the nonce back, either as the raw body or as {"challenge": "…"}. On a first registration you cannot verify its signature: the endpoint secret is created for that challenge and only returned to you in the response you are still waiting on. Verify signatures on every delivery afterwards, and on re-verification and secret rotation.
The response contains secret exactly once. See Webhooks for the signature scheme, the event catalogue, and delivery semantics.
7. Disconnecting
curl -X POST https://api.demarky.ai/oauth/revoke \
-u "$DEMARKY_CLIENT_ID:$DEMARKY_CLIENT_SECRET" \
-d token=dmk_rt_live_xxx
Revoking a refresh token ends the family but leaves already-issued access tokens valid until they expire, so revoke both if that matters to you.
A seller can also disconnect from their Demarky dashboard, and there is no webhook for it. You discover it the same way you discover an expired grant: calls return 401 and the refresh returns invalid_grant. Treat that pair as an uninstall and clean up.