# Omni API 1.0

| API version | Status | Most recent API update |
|  --- | --- | --- |
| 1.0 | Current | November 17, 2025 |


The Percents Omni API remains on version **1.0**. This release note describes the current public contract and the most recent 1.0 API update, which occurred on **November 17, 2025**.

> The documentation site may show a later publication or “last updated” timestamp when its provider, hosting, or editorial content changes. That timestamp does not by itself indicate a newer API version or a change to the 1.0 contract.


## Public API scope

- Read current, upcoming, and historical issuer-scoped offers.
- Read current and historical merchants by issuer scope.
- Request a short-lived presigned URL for an enabled transaction-file content contract.
- Upload the public `plaid-txn-stream` JSON format.
- Receive the signed `FileUploadSchemaValidation` webhook after complete file-schema validation.


## Authentication and tenant scope

API calls use an issuer-owned server-side credential:

```http
Authorization: token <token-id>:<token-secret>
```

This is not Bearer authentication. Percents resolves the credential to an issuer. Clients do not send an issuer identifier.

## Offer immutability

Published offers are immutable after creation except for `endDate`. Percents may change that end date to extend, shorten, or end an offer. A change to any other term creates a new offer with a new UUID.

The offer contract includes spatial condition, cardholder and publisher rates, calendar-only start and end dates, minimum spend, maximum earn, maximum transaction value, maximum redemptions, a `segment` of `new`, `loyal`, `lapsed`, or `null`, a required customer-facing description, and `rateIsPercent`. `rateIsPercent` controls the units of both rate fields: both are basis-point percentages when true and minor-unit fixed amounts when false.

## Plaid file upload contract

`plaid-txn-stream` is the only publicly documented `content` value. It requires a JSON envelope containing `transactions` and `accounts` arrays. Every transaction requires `percentsMerchantId` and a `segment` value of `new`, `loyal`, `lapsed`, or JSON `null`. Other content values may exist only under private partner agreements.

Requesting a presigned URL and uploading bytes are separate steps. The storage `PUT` does not mean processing has completed.

## Schema validation webhook

During regular periodic file processing, which runs no less frequently than hourly, Percents downloads each Plaid JSON upload from storage and validates its complete envelope and constituent rows. It then sends `FileUploadSchemaValidation` with the filename, discovered row count, pass/fail result, and nullable `errorMetadata`. Every transaction account must be represented in the top-level `accounts` array; missing IDs are returned in `errorMetadata.missingAccountIds`. Unknown merchant UUIDs are returned in `errorMetadata.unknownPercentsMerchantIds`. One invalid row fails the file atomically and prevents ingestion. This event does not report transaction audits or reward outcomes.

Receivers verify `X-Percents-Signature` with the separately issued `sign_` token, persist the result, deduplicate by `webhookId`, and return `2xx` promptly.