Integration Guide
A complete reference for integrating POST /v1/agent/recommend into your AI agent, chatbot, or automated shopping assistant.
Overview
The SuperPay Agent Commerce API gives AI agents access to the same card-reward ranking engine that powers the SuperPay consumer wallet. One endpoint:
- /v1/agent/recommend — Pass a transaction amount, optional MCC or category, and the user's card portfolio. Returns a ranked list of the best cards to use, with reward rate, estimated reward in cents, and a human-readable reason string your agent can speak aloud.
Quick Start
Copy-paste this cURL to fire your first request using the sandbox key from /for-agents:
curl -X POST https://superpayrewards.com/v1/agent/recommend \
-H "Authorization: Bearer sp_agent_xxxxxxxx_..." \
-H "Content-Type: application/json" \
-d '{
"amount": 87.45,
"mcc": "5411",
"merchant_name": "Whole Foods",
"cards": [
{
"id": "amex_blue_cash_preferred",
"issuer": "American Express",
"reward_type": "cashback",
"base_rate": 1,
"category_bonuses": [{ "category": "groceries", "rate": 6 }]
},
{
"id": "citi_double_cash",
"issuer": "Citi",
"reward_type": "cashback",
"base_rate": 2
}
]
}'
Expected response: the Amex Blue Cash Preferred ranks #1 at 6% for groceries, with an estimated reward of ~$5.24 on an $87.45 purchase.
Authentication
All Agent API requests require an sp_agent_… key. Send it in one of two ways:
Authorization: Bearer sp_agent_xxxxxxxx_… X-SuperPay-Key: sp_agent_xxxxxxxx_…
Both headers are accepted; Authorization: Bearer is preferred for server-to-server calls. Keys are sp_agent_ + 8 hex characters + _ + 48 hex characters.
sp_agent_… key instantly. No approval required. The sandbox key authenticates against the same recommendation engine as production keys — usage is logged identically, so it's appropriate for integration testing but not production traffic.
Request Reference
Endpoint: POST https://superpayrewards.com/v1/agent/recommend
Content-Type: application/json
Top-level fields
| Field | Type | Status | Notes |
|---|---|---|---|
| amount | number | required | USD amount, e.g. 87.45. Must be positive and ≤ 1,000,000. |
| cards | object[] | required | The user's card portfolio. See Card Portfolio Schema below. At least 1 card required. |
| mcc | string | recommended | ISO 18245 4-digit MCC, e.g. "5411". Takes precedence over category for category resolution. |
| category | string | fallback | Explicit category string. Used when MCC is absent or unmapped. See category reference. |
| merchant_name | string | recommended | Display name, e.g. "Whole Foods Market". Used for name-based category resolution when MCC is absent. |
Card Portfolio Schema
Each object in cards[] represents one card in the user's wallet:
| Field | Type | Status | Notes |
|---|---|---|---|
| id | string | required | Your internal card identifier. Returned unchanged in top_cards[].id. Max 100 chars. |
| issuer | string | required | Card issuer name, e.g. "American Express", "Chase", "Citi". Used in reason strings and display output. |
| reward_type | string | required | One of: "cashback", "points", "miles". |
| base_rate | number | required | Base reward rate as a percentage (0–20). For cashback: percent cash back (e.g. 2 for 2%). For points/miles: earn multiplier (e.g. 1.5 for 1.5x). |
| category_bonuses | object[] | optional | Array of bonus earning rates per spend category. Each: { "category": "groceries", "rate": 6 }. Rate is a percentage (cashback) or multiplier (points/miles). Omit if the card has no category bonuses. |
Example card objects
// Cashback card with category bonuses
{
"id": "amex_blue_cash_preferred",
"issuer": "American Express",
"reward_type": "cashback",
"base_rate": 1,
"category_bonuses": [
{ "category": "groceries", "rate": 6 },
{ "category": "streaming", "rate": 6 },
{ "category": "gas", "rate": 3 }
]
}
// Flat-rate cashback card (no category bonuses)
{
"id": "citi_double_cash",
"issuer": "Citi",
"reward_type": "cashback",
"base_rate": 2
}
// Points card with travel/dining bonuses
{
"id": "chase_sapphire_preferred",
"issuer": "Chase",
"reward_type": "points",
"base_rate": 1,
"category_bonuses": [
{ "category": "dining", "rate": 3 },
{ "category": "travel", "rate": 3 }
]
}
MCC → Category Reference
When mcc is absent or unmapped, pass one of these values in the category field. Common MCC codes are listed for reference:
| Category value | Description | Common MCCs |
|---|---|---|
| groceries | Supermarkets, wholesale clubs | 5411, 5412, 5441, 5451 |
| dining | Restaurants, fast food, bars, cafes | 5812, 5813, 5814 |
| gas | Gas stations, fuel | 5541, 5542, 5172 |
| travel | Airlines, hotels, car rentals, cruises | 4511, 4112, 7011, 7512 |
| entertainment | Movies, concerts, sports, streaming venues | 7832, 7922, 7941, 7993 |
| online_shopping | E-commerce, Amazon, general online retail | 5999, 5945, 5047 |
| streaming | Video and music streaming services | 4813, 7372 (Netflix, Spotify, etc.) |
| drugstores | Pharmacies, drug stores | 5912, 5122 |
| home_improvement | Home Depot, Lowe's, hardware stores | 5211, 5251, 5713 |
| transit | Subway, bus, rideshare, commuter rail | 4111, 4121, 4789 |
| shopping | Department stores, clothing, general retail | 5311, 5611, 5621, 5691 |
| other | Everything else — uses base reward rate only | (fallback) |
merchant_name keyword matching; (3) explicit category field; (4) fallback to "other". Providing both mcc and category is fine — MCC takes precedence if it maps to a known category.
Response Reference
{
"recommendation_id": "9f8b2c1a4d7e…",
"category": "groceries",
"amount_cents": 8745,
"top_cards": [
{
"rank": 1,
"id": "amex_blue_cash_preferred",
"issuer": "American Express",
"reward_type": "cashback",
"reward_rate": 6,
"reason": "6% on groceries",
"estimated_reward_cents": 524,
"estimated_reward_display": "$5.24"
},
{
"rank": 2,
"id": "citi_double_cash",
"issuer": "Citi",
"reward_type": "cashback",
"reward_rate": 2,
"reason": "2% on everything",
"estimated_reward_cents": 174,
"estimated_reward_display": "$1.74"
}
],
"top_card": { … }
}
| Field | Type | Notes |
|---|---|---|
| recommendation_id | string | Unique ID for this recommendation call. Include in support requests for faster diagnosis. |
| category | string | Resolved spend category used for ranking. |
| amount_cents | integer | Transaction amount in cents (mirrors amount × 100). |
| top_cards | object[] | Ranked list of the user's cards, best first. Contains all cards passed in the request, ranked by estimated reward value. |
| top_cards[].rank | integer | 1-indexed rank (1 = best card). |
| top_cards[].id | string | The id you passed in for this card. |
| top_cards[].issuer | string | The issuer you passed in. |
| top_cards[].reward_type | string | cashback / points / miles. |
| top_cards[].reward_rate | number | Effective rate for this transaction (percent for cashback, multiplier for points/miles). |
| top_cards[].reason | string | Human-readable explanation, e.g. "6% on groceries". Ready to use in agent responses. |
| top_cards[].estimated_reward_cents | integer | Dollar-equivalent estimated reward in cents. For points/miles, valued at $0.01/point (1 cpp). |
| top_cards[].estimated_reward_display | string | Formatted display: "$5.24" for cashback; "75 miles" for miles/points. |
| top_card | object | Deprecated. The top-ranked card. Use top_cards[0] instead. |
Rate Limits
Rate-limit headers are included on every response:
| Header | Meaning |
|---|---|
| X-RateLimit-Limit | Maximum requests per minute for your key (300). |
| X-RateLimit-Remaining | Requests remaining in the current window. |
| Retry-After | Seconds to wait before retrying (only on 429 responses). |
Implement exponential backoff when you receive a 429 RATE_LIMITED response. The window resets every 60 seconds.
Error Codes
| Status | Code | Meaning |
|---|---|---|
| 400 | INVALID_REQUEST | Request body is missing, malformed JSON, or a required field failed validation. Check the message field for details. |
| 400 | INVALID_AMOUNT | amount is missing, non-positive, or exceeds $1,000,000. |
| 400 | INVALID_CARD_DATA | One or more cards failed validation. Check card_errors[] in the response body for per-card details. |
| 401 | MISSING_API_KEY | No key in Authorization or X-SuperPay-Key header. |
| 401 | INVALID_API_KEY | Key not recognized or hash mismatch. |
| 401 | API_KEY_REVOKED | Key has been revoked — contact hello@superpayrewards.com. |
| 401 | INVALID_API_KEY_FORMAT | Key doesn't start with sp_agent_. |
| 403 | ORIGIN_NOT_ALLOWED | Browser request from unregistered origin (production keys only). |
| 422 | UNPROCESSABLE | Request was valid but could not be processed (e.g. allowed card list has no catalog matches). |
| 429 | RATE_LIMITED | Over 300 requests per minute; see Retry-After header. |
| 500 | INTERNAL_ERROR | SuperPay-side failure — safe to retry with exponential backoff. |
{
"code": "INVALID_CARD_DATA",
"message": "One or more cards failed validation.",
"card_errors": [
{ "index": 0, "id": "my_card_id", "issues": ["reward_type must be cashback, points, or miles"] }
]
}
Sandbox vs. Production
| Feature | Sandbox | Production |
|---|---|---|
| Key prefix | sp_agent_… | sp_agent_… |
| Key issuance | Instant (email verification only) | After SuperPay review (~1 business day) |
| Recommendation engine | Same as production | Same as sandbox |
| Usage logging | Same as production | Full audit trail |
| Origin allowlist enforcement | No (any origin permitted) | Yes (registered origins only) |
| Rate limit | 300 req/min (same) | 300 req/min (custom on request) |
| Key rotation API | No | POST /api/agent-partner/rotate-key |
| Appropriate for | Development & CI testing | End-user production traffic |
sp_agent_… keys like passwords. Never commit them to source control, log them, include them in LLM prompts, or expose them in client-side code. SuperPay never stores or displays the plaintext after issuance — we can only rotate, not recover.
SDK Snippets
cURL
curl -X POST https://superpayrewards.com/v1/agent/recommend \
-H "Authorization: Bearer $SUPERPAY_AGENT_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": 87.45,
"mcc": "5411",
"merchant_name": "Whole Foods",
"cards": [
{
"id": "amex_bcp",
"issuer": "American Express",
"reward_type": "cashback",
"base_rate": 1,
"category_bonuses": [{ "category": "groceries", "rate": 6 }]
},
{ "id": "citi_dc", "issuer": "Citi", "reward_type": "cashback", "base_rate": 2 }
]
}'
Node.js (fetch)
async function getBestCard({ amount, mcc, merchantName, cards }) {
const res = await fetch('https://superpayrewards.com/v1/agent/recommend', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.SUPERPAY_AGENT_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ amount, mcc, merchant_name: merchantName, cards }),
});
if (!res.ok) {
const err = await res.json().catch(() => ({}));
throw new Error(`SuperPay ${res.status}: ${err.code || 'UNKNOWN'} — ${err.message || ''}`);
}
const { top_cards, category, recommendation_id } = await res.json();
return {
bestCard: top_cards[0],
category,
recommendationId: recommendation_id,
// top_cards[0].reason → "6% on groceries" (ready for agent response)
// top_cards[0].estimated_reward_display → "$5.24"
};
}
Python (requests)
import os, requests
def get_best_card(amount, cards, mcc=None, merchant_name=None, category=None):
payload = {"amount": amount, "cards": cards}
if mcc: payload["mcc"] = mcc
if merchant_name: payload["merchant_name"] = merchant_name
if category: payload["category"] = category
resp = requests.post(
"https://superpayrewards.com/v1/agent/recommend",
headers={
"Authorization": f"Bearer {os.environ['SUPERPAY_AGENT_KEY']}",
"Content-Type": "application/json",
},
json=payload,
timeout=10,
)
resp.raise_for_status()
data = resp.json()
best = data["top_cards"][0]
return best # best["reason"] → "6% on groceries"
OpenAI function / tool definition
{
"type": "function",
"function": {
"name": "get_best_payment_card",
"description": "Returns the optimal credit card to use for a purchase based on reward rates. Always call this before the user pays.",
"parameters": {
"type": "object",
"properties": {
"amount": { "type": "number", "description": "Purchase amount in USD" },
"merchant_name": { "type": "string", "description": "Merchant or store name" },
"mcc": { "type": "string", "description": "ISO 18245 MCC code if available" }
},
"required": ["amount"]
}
}
}
In your tool handler, pass the user's card portfolio from your agent's memory alongside the LLM-extracted fields, then inject top_cards[0].reason back into the assistant message.
Key Rotation
Rotate a lost or compromised production key via the API — no email required:
POST https://superpayrewards.com/api/agent-partner/rotate-key
Authorization: Bearer sp_agent_xxxxxxxx_...
Content-Type: application/json
{} # empty body
The response contains your new plaintext key — store it immediately. The old key is revoked atomically.
{
"message": "Key rotated successfully.",
"new_key": "sp_agent_newprefix_…",
"new_key_prefix": "sp_agent_newprefix"
}
To view your current partner profile (name, email, tier, key prefix): GET /api/agent-partner/me with the same bearer token.
Questions? Email hello@superpayrewards.com or visit /for-agents. Include your recommendation_id for faster support.