Partner apps and OAuth
For software companies that connect to many pros. A pro connecting their own tools uses an API key instead.
Access levels
| Sandbox | Verified | |
|---|---|---|
| When | As soon as you create the app | After we check your company |
| Can connect | Your own pro account and test accounts | Any pro who taps Allow |
| Rate limit | 600 requests an hour per pro | 3,000 an hour per pro |
| Scopes | All | All |
To get verified, open your app at /partners and tap Ask for verification. We check your company and website, your privacy policy, your contact, and what you said the app does with pros' data. We can suspend an app at any time; suspending stops every token at once.
1. Create the app
At /partners: name, company, website, privacy policy, contact email, what the app does, redirect addresses (https; http only for localhost) and scopes. You get a client ID (lmc_...) and a client secret (lms_..., shown once; you can make a new one).
2. Send the pro to Connect
https://lead-marketplace.mcrdminted.workers.dev/connect
?response_type=code
&client_id=lmc_...
&redirect_uri=https://your-app.example.com/oauth/callback
&scope=leads:read leads:write messages:read messages:write
&state=<random value you check later>
&code_challenge=<BASE64URL(SHA256(code_verifier))>
&code_challenge_method=S256(URL-encode the values. The address moves to the brand domain at launch.) The pro signs in, sees your app, your company, verified or sandbox, and each thing you ask for in plain words, then taps Allow or Cancel. Leave out scope to ask for every scope your app has. PKCE with S256 is required.
We send the pro back to redirect_uri?code=lma_...&state=..., or ?error=access_denied&state=.... Check state. An unknown app or an unregistered redirect_uri is shown as an error on our page and never redirected.
3. Swap the code for tokens
POST https://zqawbbylldymdfzineqq.supabase.co/functions/v1/pro-api/oauth/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic base64(client_id:client_secret)
grant_type=authorization_code&code=lma_...&redirect_uri=https://your-app.example.com/oauth/callback&code_verifier=...(client_id and client_secret may go in the body instead of Basic.)
{"access_token":"lmo_...","token_type":"Bearer","expires_in":3600,"refresh_token":"lmr_...","scope":"leads:read leads:write messages:read messages:write","business_id":"0d3b7c4e-..."}Codes last 10 minutes and work once. Using a code twice fails and revokes the tokens issued from it. Store tokens per business_id.
4. Call the API
Authorization: Bearer lmo_... on any endpoint, limited to the granted scopes.
5. Refresh
grant_type=refresh_token&refresh_token=lmr_...Returns a new access token and a new refresh token; the old refresh token stops working. Refresh tokens last 180 days from issue.
Revoke
POST /oauth/revoke token=<access or refresh token> (client authentication as above)A pro can also disconnect your app in Settings, Connect your software, Connected apps; your tokens stop at once and calls return 401.
Your app's webhook
Set one webhook address on your app at /partners. You get events for every pro who connected your app, each with business_id, only for what that pro allowed (for example message.* needs messages:read, charge.* and credit.* need billing:read). Signed with your app's own secret, same format as pro webhooks. After about 3 days of failures it turns off and we email your contact address.
Errors
Token errors follow OAuth 2.0: invalid_client (401), invalid_grant, unauthorized_client, access_denied, unsupported_grant_type (400), with error_description where useful.