PerkSpringAPI DocsBeta

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:

EmailResult
test_pass@perkspring.comverified: true
test_fail@perkspring.comverified: false
test_error@perkspring.com500 error
test_slow@perkspring.comdelayed response
POST

/api/verify

Retailer

Verify 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.

POST

/api/upload

Membership

Upload 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.

GET

/api/upload-status

Membership

Poll 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.

POST

/api/erasure

Membership

Remove 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.

GET

/api/orgs

Retailer

List 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"
    }
  ]
}
GET

/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 }
  ]
}
POST

/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."
}
GET

/api/keys

List all API keys for your organization. Key values are not returned — only metadata (label, environment, status, creation date).

PATCH

/api/keys

Revoke or reactivate an API key.

Request

{
  "keyId": "a1b2c3d4-...",
  "active": false
}

Response

{
  "keyId": "a1b2c3d4-...",
  "active": false
}

Revoked keys stop authenticating immediately.

GET

/api/partnerships

List all partnerships for your organization, with partner name and type.

POST

/api/partnerships

Create a new partnership. Must be between a retailer and a membership organization.

Request

{
  "partnerOrgId": "b804f10e-3300-4670-..."
}
PATCH

/api/partnerships

Activate or deactivate a partnership. Either party can change the status.

Request

{
  "partnershipId": "c905a21f-4411-5781-...",
  "status": "inactive"
}
GET

/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

CodeMeaning
200OK — request succeeded
202Accepted — upload is processing in the background
400Bad Request — missing or invalid parameters
401Unauthorized — missing or invalid API key
403Forbidden — wrong org type for this endpoint
404Not Found — resource doesn't exist
409Conflict — duplicate (partnership, upload in progress, or idempotency key collision)
413Payload Too Large — file exceeds 10 MB or 500K rows
429Too Many Requests — rate limit exceeded
500Internal Server Error