# Quickstart

This walkthrough validates authentication, catalog access, file upload, and result delivery in sandbox.

## 1. Prepare credentials

Obtain the sandbox API token, confirm the calling IP is allowlisted, and configure a public HTTPS webhook endpoint with Percents.

```bash
export PERCENTS_API_BASE='https://sandbox.percents.com'
export PERCENTS_API_TOKEN='tok_your_id:api_your_secret'
```

## 2. Read merchants and offers

```bash
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.

## 3. Request a Plaid upload URL

```bash
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:

```json
{
  "url": "https://object-storage.example/presigned-upload"
}
```

## 4. Upload the file

```bash
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.

## 5. Receive the result

Return a successful `2xx` response after validating the webhook signature and durably recording the event. Process each `webhookId` idempotently.

During regular periodic file processing, which runs no less frequently than hourly, Percents downloads and validates the complete file and sends `FileUploadSchemaValidation`. A passing result acknowledges the file contract only; it is not an audit or reward result.