App installation
Flow overview
The diagram below shows every call involved in the installation, including the server-to-server call that Appmax makes to your validation URL during the health check.
INFO
Step 4 combines two actions in a single request:
- You call
POST /app/client/generatewith the authorized hash. - While processing that call, Appmax makes a server-to-server POST to the validation URL you configured.
- Your validation URL must respond with HTTP 200 and an
external_id(UUID) — only then will Appmax return the merchant credentials.
The external_id you return here is not throwaway — it becomes the current identifier for that store and is used as the external-id header on every CDN call from the front-end. Every new installation requires a new value from Appmax, and the previous one stops being valid. See external-id to understand where this value is consumed later.
Installation flow
Before making any request, obtain the access token using the app credentials.
Endpoint: POST https://auth.appmax.com.br/oauth2/token
curl --location 'https://auth.appmax.com.br/oauth2/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'client_id=CLIENT_ID' \
--data-urlencode 'client_secret=CLIENT_SECRET'Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6Ikp...",
"token_type": "Bearer",
"expires_in": 3600
}With the app access token, generate an authorization hash to redirect the merchant.
Endpoint: POST https://api.appmax.com.br/app/authorize
curl --location 'https://api.appmax.com.br/app/authorize' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_TOKEN' \
--data '{
"app_id": "APP_ID",
"external_key": "EXTERNAL_KEY",
"url_callback": "URL_CALLBACK",
"domain_name": "subdomain.yourdomain.com"
}'Response:
{
"data": {
"token": "12083w36219d223f33ecf48f2a7f5ccf143b0bc554"
}
}Request parameters:
app_idstringobrigatorioApp UUID of the application. Use the UUID, not the Numerical ID (see warning below).
external_keystringobrigatorioKey provided by the partner platform to identify the origin of the installation (e.g., store_id, merchant_id).
url_callbackstringobrigatorioURL where the user will be redirected after authorization.
domain_namestringopcionalThe store's domain (subdomain + domain, e.g. mystore.myintegration.com). To send more than one domain in the same installation, use domain_names (array) instead of domain_name.
domain_name is essential if you're going to use Apple Pay
Not required for the installation itself, but it's essential if the store will process Apple Pay payments: it's from this domain, informed here, that Appmax registers the domain with Apple — part of enabling Apple Pay on the customer's device. Without it, the Apple Pay button won't work for that store, even if the rest of the integration is correct. You still need to publish the .well-known file on the domain — see Domain configuration for Apple Pay and Apple Pay payment.
App UUID vs App Numerical ID
Your application has two identifiers in the panel:
- App UUID — e.g.,
8f2c1d3e-5a4b-4c7d-9e1f-2a3b4c5d6e7f - App Numerical ID — e.g.,
699
Use the App UUID in all endpoints of the official documentation (including this POST /app/authorize), except in POST /app/client/generate — or whenever a field is explicitly marked as "Numerical ID".
Confusing the two is one of the most common causes of 422 Unprocessable Entity at this stage. If you're receiving this error, check which of the two IDs you're sending.
Redirect the user to the authorization URL, replacing HASH with the generated token.
| Environment | Redirect URL |
|---|---|
| Sandbox | https://breakingcode.sandboxappmax.com.br/appstore/integration/HASH |
| Production | https://admin.appmax.com.br/appstore/integration/HASH |
WARNING
The redirect is essential for the merchant to authorize the installation. Without this step, the merchant credentials will not be generated.
The merchant can pick a store that already exists
On this screen the merchant can select an existing store from their account instead of creating a new one. The API contract does not change, but the store bound to the installation may be one that already existed — including one where your app was installed before. See Reusing an existing store on installation.
After the merchant authorizes the installation, use the hash to generate the credentials.
Endpoint: POST https://api.appmax.com.br/app/client/generate
curl --location 'https://api.appmax.com.br/app/client/generate' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_TOKEN' \
--data '{
"token": "12083w36219d223f33ecf48f2a7f5ccf143b0bc554"
}'Response:
{
"data": {
"client": {
"client_id": "MERCHANT_CLIENT_ID",
"client_secret": "MERCHANT_CLIENT_SECRET"
}
}
}WARNING
The hash can only be used once. The generated credentials are valid indefinitely until the app is uninstalled.
Health check
During this request, Appmax performs a health check to complete the installation. Appmax sends a POST to the validation URL specified in the "Validation URL" field when creating the application.
Payload sent:
{
"app_id": 123,
"client_id": "MERCHANT_CLIENT_ID",
"client_secret": "MERCHANT_CLIENT_SECRET",
"client_key": "EXTERNAL_KEY",
"external_key": "EXTERNAL_KEY"
}Payload fields:
app_idintegerobrigatorioThe application's App Numerical ID (numeric ID, e.g. 123). Note: this is the Numerical ID, not the UUID — unlike POST /app/authorize. It is the only required field in the payload: it is always present in every health check call.
client_idstringopcionalOptional. Client ID generated for the merchant (API credential). May not be sent — do not treat its absence as an error.
client_secretstringopcionalOptional. Client Secret generated for the merchant (API credential). May not be sent — do not treat its absence as an error.
client_keystringopcionalOptional. Same value as external_key (kept for backwards compatibility). May not be sent — do not treat its absence as an error.
external_keystringopcionalOptional. Key provided by the merchant during installation for identification. May not be sent — do not treat its absence as an error.
app_id: Numerical ID, and the only required field
In the health check payload, only app_id is required — all other fields are optional and may be absent. The app_id sent is the App Numerical ID (e.g. 123), not the UUID. Your handler should only validate the presence of app_id and treat the other fields as optional.
Expected response — HTTP 200:
{
"external_id": "37bb0791-ee0b-457d-860c-186e32978bcd",
"alias": "My Store"
}You are the one who generates this external_id — Appmax only persists it, bound to the store. The same UUID later comes back as the external-id header on every front-end call (tokenization, Apple Pay) — it is the identifier for that store on the CDN. Generate a new UUID on every health check request: repeated values are rejected by Appmax — if the external_id it receives already exists in the database, it is discarded and automatically replaced by the installation's client_id. Store it in your database the moment you generate it and, whenever a new health check happens, always keep the latest value and discard the previous one. Full reference at external-id.
| Field | Type | Required | Description |
|---|---|---|---|
external_id | string (UUID) | Yes | Unique installation ID in your system. Must be a valid UUID (v1 through v5) and unique across all installations. This same value is later sent as the external-id header on CDN calls from the front-end. |
alias | string | No | Site/store name. If provided, it will be used as the display name in Appmax. |
WARNING
The health check is mandatory to complete the installation. If your URL is not reachable, returns a status other than 200, or fails to return a valid UUID external_id in the body, the installation is considered failed — /app/client/generate aborts with 500 and no credentials are issued. Test your URL at Validate your validation URL before starting the installation flow.
DANGER
The external_id must be a valid UUID (e.g., 37bb0791-ee0b-457d-860c-186e32978bcd). For apps in the Payments and Security category, the external_id is strictly required — the installation will fail if it is not sent.
INFO
Store the external_id in your database to identify the binding between the merchant and your application. Each installation must generate a different external_id — duplicate values will be rejected.
Same value, from generation to front-end use
The external_id you return on the health check is exactly the same value that goes as the external-id header on every front-end call via CDN. Generate a new UUID on every health check request — repeated values are rejected by Appmax and automatically replaced by the installation's client_id. Store it the moment you generate it and, on every new health check, replace the stored value with the most recent one, discarding the previous. Usage details, AppCheckout.init parameter, and error diagnosis at external-id.
With the merchant credentials (client_id and client_secret) generated in the previous step, authenticate to obtain the merchant access token. This is the token that authorizes transactional operations (/v1/customers, /v1/orders, /v1/payments/*).
Endpoint: POST https://auth.appmax.com.br/oauth2/token
curl --location 'https://auth.appmax.com.br/oauth2/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'client_id=MERCHANT_CLIENT_ID' \
--data-urlencode 'client_secret=MERCHANT_CLIENT_SECRET'Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6Ikp...",
"token_type": "Bearer",
"expires_in": 3600
}Use the returned access_token as the Authorization: Bearer YOUR_TOKEN header on the API's transactional calls.
WARNING
This is the same endpoint used to authenticate the application (step 1) — the difference is sending the merchant's client_id/client_secret. Sending the app credentials here results in 401 on the /v1/* routes.
INFO
The token expires in 1 hour and the API does not use refresh tokens — when it expires, just repeat this same request. The merchant credentials, on the other hand, are permanent (until uninstallation). Understand both credential types at Authentication.
Summary
- Obtain the app token using the app credentials.
- Authorize the installation by generating a hash.
- Redirect the merchant to authorize.
- Generate credentials with
POST /app/client/generate— during this request, Appmax sends the health check to your validation URL. Respond with HTTP 200 and theexternal_idto complete the installation. - Authenticate with the merchant credentials at
POST /oauth2/tokento obtain the access token and transact on the API (/v1/*).
Troubleshooting
The most common errors during installation and how to fix them.
422 Unprocessable Entity on POST /app/authorize
Most likely cause: you're sending the App Numerical ID instead of the App UUID in the app_id field.
Fix: copy the App UUID from the panel (format 8f2c1d3e-5a4b-...) and send it in that field. The Numerical ID (integer) is only used in POST /app/client/generate, never here.
422 Unprocessable Entity on POST /app/client/generate
Possible causes:
- Invalid or already-used hash — each hash can only be used once. If you tried and it failed, you need to generate a new hash with
POST /app/authorize. - Merchant didn't go through the redirect — you skipped step 3. Without the redirect and authorization in the panel, the hash doesn't become valid for generating credentials.
- Expired hash — hashes have limited lifetime. Generate a new one and complete the flow immediately.
Fix: follow the full flow in order: authorize → redirect → authorization in the panel → client/generate.
500 Internal Server Error on POST /app/client/generate
Most likely cause: the health check failed. Appmax tried to call your validation URL and:
- The URL didn't respond (timeout, DNS, firewall, or wrong URL in the panel).
- The URL responded with a status other than
200. - The URL responded with
200but without a validexternal_idin UUID format in the JSON body.
Fix:
- Verify the validation URL is correct in the app panel.
- Test it manually with
curl— it must respond to POST with HTTP 200 and JSON containing{"external_id": "<valid UUID>"}. - Check your server logs for the POST that Appmax sent.
- Make sure you're not using
localhostor a private URL — Appmax needs to reach the URL publicly.
I received the POST on the validation URL, but POST /app/client/generate returns an error
Cause: your validation URL received the payload but didn't respond correctly — most likely:
- Responded with a status other than
200(e.g.,204,301,500). - Returned
200but without JSON containingexternal_idin the body. - Returned an
external_idthat is not a valid UUID or already used in another installation.
Fix: your validation URL handler must:
- Return HTTP 200 explicitly.
- Include a JSON body with
{"external_id": "<UUID v1-v5>"}. - Generate a unique
external_idper installation (e.g.,uuid.v4()).
The validation URL is on localhost (development)
Appmax cannot reach private URLs. During development:
- Use a tunnel service like ngrok, beeceptor, or similar.
- Configure the public URL in the app panel.
- When publishing to production, update to the final URL.
App credentials (client_id/client_secret) don't work on /v1/* routes
Cause: app credentials only work on POST /app/authorize and POST /app/client/generate. For transactional routes (/v1/customers, /v1/orders, etc.), use the merchant credentials generated in step 4.
See Authentication to understand both credential types.
Errors using external_id on the front-end (CDN)
The errors on this page cover the generation of the external_id during the health check. If the installation completed successfully but the front-end (tokenization, Apple Pay) is returning 401 Missing Authorization token, 404 Merchant not found, 404 Client id not found, or External ID is required, those are usage errors of the external-id header — the full diagnosis table is at Common errors and diagnosis.
More help
If your error is not listed above:
- Check the FAQ.
- Review Rate Limit if you're getting
429. - Use the
appmax-docsMCP server (diagnose_errortool) passing the status code and the endpoint for an automated diagnosis.