SuperPay Agent Commerce API

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:

No cardholder PII. The API works entirely on card metadata you supply per-request. You send card IDs, issuers, reward types, and rates — never a cardholder name, account number, CVV, or transaction ID. SuperPay does not receive or store any user-identifying data through this endpoint.

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.

Sandbox key. Sign up at /for-agents#signup to get a free sandbox 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

FieldTypeStatusNotes
amountnumberrequiredUSD amount, e.g. 87.45. Must be positive and ≤ 1,000,000.
cardsobject[]requiredThe user's card portfolio. See Card Portfolio Schema below. At least 1 card required.
mccstringrecommendedISO 18245 4-digit MCC, e.g. "5411". Takes precedence over category for category resolution.
categorystringfallbackExplicit category string. Used when MCC is absent or unmapped. See category reference.
merchant_namestringrecommendedDisplay 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:

FieldTypeStatusNotes
idstringrequiredYour internal card identifier. Returned unchanged in top_cards[].id. Max 100 chars.
issuerstringrequiredCard issuer name, e.g. "American Express", "Chase", "Citi". Used in reason strings and display output.
reward_typestringrequiredOne of: "cashback", "points", "miles".
base_ratenumberrequiredBase 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_bonusesobject[]optionalArray 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 valueDescriptionCommon MCCs
groceriesSupermarkets, wholesale clubs5411, 5412, 5441, 5451
diningRestaurants, fast food, bars, cafes5812, 5813, 5814
gasGas stations, fuel5541, 5542, 5172
travelAirlines, hotels, car rentals, cruises4511, 4112, 7011, 7512
entertainmentMovies, concerts, sports, streaming venues7832, 7922, 7941, 7993
online_shoppingE-commerce, Amazon, general online retail5999, 5945, 5047
streamingVideo and music streaming services4813, 7372 (Netflix, Spotify, etc.)
drugstoresPharmacies, drug stores5912, 5122
home_improvementHome Depot, Lowe's, hardware stores5211, 5251, 5713
transitSubway, bus, rideshare, commuter rail4111, 4121, 4789
shoppingDepartment stores, clothing, general retail5311, 5611, 5621, 5691
otherEverything else — uses base reward rate only(fallback)
Resolution order. (1) MCC → internal mapping; (2) 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": { … }
}
FieldTypeNotes
recommendation_idstringUnique ID for this recommendation call. Include in support requests for faster diagnosis.
categorystringResolved spend category used for ranking.
amount_centsintegerTransaction amount in cents (mirrors amount × 100).
top_cardsobject[]Ranked list of the user's cards, best first. Contains all cards passed in the request, ranked by estimated reward value.
top_cards[].rankinteger1-indexed rank (1 = best card).
top_cards[].idstringThe id you passed in for this card.
top_cards[].issuerstringThe issuer you passed in.
top_cards[].reward_typestringcashback / points / miles.
top_cards[].reward_ratenumberEffective rate for this transaction (percent for cashback, multiplier for points/miles).
top_cards[].reasonstringHuman-readable explanation, e.g. "6% on groceries". Ready to use in agent responses.
top_cards[].estimated_reward_centsintegerDollar-equivalent estimated reward in cents. For points/miles, valued at $0.01/point (1 cpp).
top_cards[].estimated_reward_displaystringFormatted display: "$5.24" for cashback; "75 miles" for miles/points.
top_cardobjectDeprecated. The top-ranked card. Use top_cards[0] instead.

Rate Limits

Rate-limit headers are included on every response:

HeaderMeaning
X-RateLimit-LimitMaximum requests per minute for your key (300).
X-RateLimit-RemainingRequests remaining in the current window.
Retry-AfterSeconds 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

StatusCodeMeaning
400INVALID_REQUESTRequest body is missing, malformed JSON, or a required field failed validation. Check the message field for details.
400INVALID_AMOUNTamount is missing, non-positive, or exceeds $1,000,000.
400INVALID_CARD_DATAOne or more cards failed validation. Check card_errors[] in the response body for per-card details.
401MISSING_API_KEYNo key in Authorization or X-SuperPay-Key header.
401INVALID_API_KEYKey not recognized or hash mismatch.
401API_KEY_REVOKEDKey has been revoked — contact hello@superpayrewards.com.
401INVALID_API_KEY_FORMATKey doesn't start with sp_agent_.
403ORIGIN_NOT_ALLOWEDBrowser request from unregistered origin (production keys only).
422UNPROCESSABLERequest was valid but could not be processed (e.g. allowed card list has no catalog matches).
429RATE_LIMITEDOver 300 requests per minute; see Retry-After header.
500INTERNAL_ERRORSuperPay-side failure — safe to retry with exponential backoff.
INVALID_CARD_DATA response shape:
{
  "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

FeatureSandboxProduction
Key prefixsp_agent_…sp_agent_…
Key issuanceInstant (email verification only)After SuperPay review (~1 business day)
Recommendation engineSame as productionSame as sandbox
Usage loggingSame as productionFull audit trail
Origin allowlist enforcementNo (any origin permitted)Yes (registered origins only)
Rate limit300 req/min (same)300 req/min (custom on request)
Key rotation APINoPOST /api/agent-partner/rotate-key
Appropriate forDevelopment & CI testingEnd-user production traffic
Key security. Treat 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.