This walkthrough validates authentication, catalog access, file upload, and result delivery in sandbox.
Obtain the sandbox API token, confirm the calling IP is allowlisted, and configure a public HTTPS webhook endpoint with Percents.
export PERCENTS_API_BASE='https://sandbox.percents.com'
export PERCENTS_API_TOKEN='tok_your_id:api_your_secret'curl --fail-with-body \
--header "Authorization: token ${PERCENTS_API_TOKEN}" \
"${PERCENTS_API_BASE}/api/v1/omni/merchant"
curl --fail-with-body \
--header "Authorization: token ${PERCENTS_API_TOKEN}" \
"${PERCENTS_API_BASE}/api/v1/omni/offer"Persist merchant and offer UUIDs exactly as returned. Do not infer access to a merchant from its name.
curl --fail-with-body --get \
--header "Authorization: token ${PERCENTS_API_TOKEN}" \
--data-urlencode 'content=plaid-txn-stream' \
--data-urlencode 'fileType=json' \
--data-urlencode 'fileName=transactions-2026-08-10.json' \
"${PERCENTS_API_BASE}/api/fileUpload"The response contains a presigned URL that expires after 15 minutes:
{
"url": "https://object-storage.example/presigned-upload"
}curl --fail-with-body \
--request PUT \
--header 'Content-Type: application/json' \
--data-binary @transactions.json \
'PRESIGNED_URL_FROM_THE_PREVIOUS_RESPONSE'Do not send the Percents API token to the presigned URL. The URL itself grants temporary permission to upload that single object.
Return a successful 2xx response after validating the webhook signature and durably recording the event. Process each webhookId idempotently.
The result is not delivered on a fixed schedule. Automated checks and human oversight must finish first. Contact Percents if no result webhook arrives within three business days after a successful file upload.