Authentication
All API requests require a Bearer token in the Authorization header:
curl -X POST https://tryperkspring.com/api/verify \ -H "Authorization: Bearer pk_your_api_key" \ -H "Content-Type: application/json"
API keys are scoped to your organization and environment (sandbox or live). Create and manage keys from the dashboard or via the API.
API Conventions
Versioning
Send a PerkSpring-Version header with a date-based version (e.g. 2026-06-18). Your org is pinned to a version on first request. All responses include PerkSpring-Version and X-API-Version-Current headers.
Idempotency
For POST requests, send an Idempotency-Key header (max 256 chars). If you retry with the same key within 24 hours, PerkSpring returns the cached response with an Idempotent-Replayed: true header.
Rate Limiting
The verification endpoint is rate-limited to 100 requests per minute per organization. If you exceed the limit, you'll receive a 429 Too Many Requests response.
Sandbox Testing
Sandbox API keys return canned responses for these test emails:
| Result | |
|---|---|
| test_pass@perkspring.com | verified: true |
| test_fail@perkspring.com | verified: false |
| test_error@perkspring.com | 500 error |
| test_slow@perkspring.com | delayed response |
/api/verify
RetailerVerify if an email belongs to a membership organization's roster.
Request
{
"email": "jane.smith@example.com",
"membershipOrgId": "5739777b-8a89-417d-..."
}Response
{
"verified": true,
"membershipOrgId": "5739777b-8a89-417d-..."
}The email is hashed server-side with the membership org's salt and compared against stored hashes. Plain-text emails are never stored or logged. Requires an active partnership between your organization and the target membership org.
/api/upload
MembershipUpload a CSV roster of member emails. Each email is salted, hashed (SHA-256), and stored. Processing runs in the background.
Request
curl -X POST https://tryperkspring.com/api/upload \ -H "Authorization: Bearer pk_your_api_key" \ -F "file=@members.csv"
CSV format: one email address per row, no header required. Max file size: 10 MB. Max rows: 500,000.
Response (202 Accepted)
{
"rosterVersionId": "a1b2c3d4-...",
"total": 1530,
"status": "processing"
}Use the rosterVersionId to poll for completion via the upload-status endpoint.
/api/upload-status
MembershipPoll the status of a roster upload.
Request
curl https://tryperkspring.com/api/upload-status?versionId=a1b2c3d4-... \ -H "Authorization: Bearer pk_your_api_key"
Response
{
"id": "a1b2c3d4-...",
"status": "active",
"totalRows": 1530,
"processedRows": 1530,
"error": null,
"createdAt": "2026-06-19T...",
"activatedAt": "2026-06-19T..."
}Status values: processing, active, failed, archived.
/api/erasure
MembershipRemove a member's data from the platform (GDPR Article 17 right to erasure). Deletes all matching hashes and redacts verification history.
Request
{
"email": "jane.smith@example.com"
}Response
{
"erased": true,
"hashesRemoved": 1
}All erasure requests are logged in an immutable audit trail for compliance.
/api/orgs
RetailerList membership organizations available for verification. Returns only orgs with an active partnership to your organization.
Response
{
"organizations": [
{
"id": "5739777b-8a89-417d-...",
"name": "AAA Northeast",
"memberCount": 12450,
"createdAt": "2026-06-18T07:42:56Z"
}
]
}/api/usage
Get verification usage metrics for your organization.
Response
{
"total": 4820,
"thisMonth": 1230,
"prevMonth": 980,
"daily": [
{ "date": "2026-09-01", "count": 45 },
{ "date": "2026-09-02", "count": 62 }
]
}/api/keys
Create a new API key for your organization.
Request
{
"label": "production",
"environment": "live"
}Response
{
"key": "pk_a1b2c3d4...",
"label": "production",
"environment": "live",
"note": "Store this key securely — it cannot be retrieved again."
}/api/keys
List all API keys for your organization. Key values are not returned — only metadata (label, environment, status, creation date).
/api/keys
Revoke or reactivate an API key.
Request
{
"keyId": "a1b2c3d4-...",
"active": false
}Response
{
"keyId": "a1b2c3d4-...",
"active": false
}Revoked keys stop authenticating immediately.
/api/partnerships
List all partnerships for your organization, with partner name and type.
/api/partnerships
Create a new partnership. Must be between a retailer and a membership organization.
Request
{
"partnerOrgId": "b804f10e-3300-4670-..."
}/api/partnerships
Activate or deactivate a partnership. Either party can change the status.
Request
{
"partnershipId": "c905a21f-4411-5781-...",
"status": "inactive"
}/api/health
Health check endpoint. No authentication required.
Response
{ "status": "ok" }Returns 503 with {"status": "degraded", "db": "unreachable"} if the database is down.
Error Codes
| Code | Meaning |
|---|---|
| 200 | OK — request succeeded |
| 202 | Accepted — upload is processing in the background |
| 400 | Bad Request — missing or invalid parameters |
| 401 | Unauthorized — missing or invalid API key |
| 403 | Forbidden — wrong org type for this endpoint |
| 404 | Not Found — resource doesn't exist |
| 409 | Conflict — duplicate (partnership, upload in progress, or idempotency key collision) |
| 413 | Payload Too Large — file exceeds 10 MB or 500K rows |
| 429 | Too Many Requests — rate limit exceeded |
| 500 | Internal Server Error |