Setting up the details behind the scenes. This will only take a moment.
Occassionly partner API is prepaid-credit based. Card issuance consumes credits from the partner wallet and card verification returns deterministic status.
Every request must include: X-Partner-Id, X-Api-Key, X-Timestamp, X-Signature.
Signature canonical string: timestamp + method + path + body, HMAC SHA-256 using the partner API secret.
Standard response envelope: { success: boolean, data?: ..., error?: ... }.
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /api/v1/partner/credits | Read partner credit balance |
| POST | /api/v1/partner/events | Create/link partner event |
| GET | /api/v1/partner/events | List partner events |
| GET | /api/v1/partner/events/{partner_event_id} | Get single partner event |
| GET | /api/v1/partner/events/{partner_event_id}/cards | List all cards issued under one event |
| GET | /api/v1/partner/activities | Partner activity/history feed |
| POST | /api/v1/partner/cards | Issue card (consumes configured credits) |
| POST | /api/v1/partner/cards/{occ_card_id}/revoke | Revoke previously issued card |
| POST | /api/v1/partner/cards/{occ_card_id}/approve | Approve/reactivate previously revoked card |
| POST | /api/v1/partner/verify | Verify QR payload |
| POST | /api/partners/verify-card | Public partner QR verification helper |
| POST | /api/partners/verify-code | Verify Guest Access code (GA-XXXXX / GA-XXXXXX) |
| GET | /api/v1/partner/webhooks/outbox | List webhook delivery events for partner |
| POST | /api/v1/partner/webhooks/outbox/retry-due | Run retries for due failed webhook deliveries |
| POST | /api/v1/partner/webhooks/outbox/{webhook_event_id}/replay | Manual replay of one webhook event |
Outgoing webhooks are signed via X-Occassionly-Timestamp and X-Occassionly-Signature.
Canonical string: timestamp + POST + /webhooks/partner + body.
INVALID_AUTH_HEADERS - missing auth headers.
EXPIRED_TIMESTAMP - timestamp out of replay window.
PARTNER_UNAUTHORIZED - inactive or invalid partner/key.
INVALID_SIGNATURE - request signature mismatch.
VALIDATION_ERROR - payload schema failed.
INSUFFICIENT_CREDITS - cannot issue card due to balance.
EVENT_NOT_LINKED - partner event is not linked.
CARD_NOT_FOUND - card does not exist for partner.
WEBHOOK_EVENT_NOT_FOUND - replay target missing.
RATE_LIMITED - too many requests.
POST /api/v1/partner/cards
{
"issue_ref": "evt_12345_guest_7788",
"partner_event_id": "evt_12345",
"partner_guest_id": "guest_7788",
"guest_name": "Ibrahim Musa",
"email": "ibrahim@example.com"
}
200
{
"success": true,
"data": {
"occ_card_id": "occ_card_xxx",
"status": "issued",
"card_url": "https://occassionly.com/c/occ_card_xxx",
"qr_payload": "OCCP|occ_card_xxx|..."
}
}Use the official SDK to avoid manual signature implementation.
If you pass baseUrl manually, use host only (for example http://10.136.40.83:5000), not /api.
import {
createClient,
extractPartnerQrPayload,
normalizeGuestAccessCode,
} from "@occassionly/partner-sdk";
const client = createClient({
partnerId: process.env.OCC_PARTNER_ID!,
apiKeyId: process.env.OCC_API_KEY_ID!,
apiSecret: process.env.OCC_API_SECRET!,
});
const credits = await client.getCredits();
const events = await client.listEvents({ limit: 20 });
const event = await client.getEvent("evt_12345");
const eventCards = await client.listEventCards("evt_12345", { limit: 100 });
const activity = await client.listActivities({ limit: 50 });
const linked = await client.createEvent({
partner_event_id: "evt_12345",
event_name: "Wedding Reception",
timezone: "Africa/Lagos",
});
const card = await client.issueCard({
issue_ref: "evt_12345_guest_001",
partner_event_id: "evt_12345",
partner_guest_id: "guest_001",
guest_name: "Ibrahim Musa",
email: "ibrahim@example.com",
});// partner-side verify (signed v1 endpoint)
const qrPayload = extractPartnerQrPayload(scannedRawText);
if (qrPayload) {
const verified = await client.verifyCardByQrPayload(qrPayload);
console.log(verified);
}
// GA code fallback/manual entry
const gaCode = normalizeGuestAccessCode("ga1a2b3");
if (gaCode) {
const verified = await client.verifyCardByAccessCode(gaCode);
console.log(verified);
}
// approve a revoked card
await client.approveCard("occ_card_ab12cd34");GET /api/v1/partner/credits
200
{
"success": true,
"data": {
"partner_id": "partner_demo",
"credits_total": 500,
"credits_used": 120,
"credits_remaining": 380
}
}
POST /api/v1/partner/events
{
"partner_event_id": "evt_12345",
"event_name": "Wedding Reception",
"timezone": "Africa/Lagos"
}
200
{
"success": true,
"data": {
"occ_event_id": "67f0f7c2e7f8f92b4d0e0c11",
"status": "active"
}
}
GET /api/v1/partner/events?limit=20&page=1
200
{
"success": true,
"data": {
"items": [
{
"partner_event_id": "evt_12345",
"occ_event_id": "67f0f7c2e7f8f92b4d0e0c11",
"event_name": "Wedding Reception",
"status": "active",
"cards_issued": 42,
"cards_revoked": 1
}
],
"total": 1,
"page": 1,
"limit": 20
}
}
POST /api/v1/partner/cards
{
"issue_ref": "evt_12345_guest_001",
"partner_event_id": "evt_12345",
"partner_guest_id": "guest_001",
"guest_name": "Ibrahim Musa",
"email": "ibrahim@example.com"
}
200
{
"success": true,
"data": {
"occ_card_id": "occ_card_ab12cd34",
"status": "issued",
"card_url": "https://occassionly.com/c/occ_card_ab12cd34",
"qr_payload": "OCCP|occ_card_ab12cd34|ea73..."
}
}
POST /api/v1/partner/verify
{
"qr_payload": "OCCP2|occ_card_ab12cd34|Ibrahim%20Musa|guest_001|evt_12345|Wedding%20Reception|ea73..."
}
200
{
"success": true,
"data": {
"status": "valid",
"occ_card_id": "occ_card_ab12cd34"
}
}
POST /api/partners/verify-code
{
"code": "GA-1A2B3C"
}
200
{
"success": true,
"data": {
"valid": true,
"source": "partner",
"verify_mode": "guest_access_code",
"guest_access_code": "GA-1A2B3C",
"occ_card_id": "occ_card_ab12cd34",
"partner_guest_id": "guest_001"
}
}
POST /api/v1/partner/cards/{occ_card_id}/revoke
{
"reason": "manual_revoke"
}
200
{
"success": true,
"data": {
"occ_card_id": "occ_card_ab12cd34",
"status": "revoked"
}
}
GET /api/v1/partner/webhooks/outbox
200
{
"success": true,
"data": {
"items": [],
"next_cursor": null
}
}
Common error:
{
"success": false,
"error": {
"code": "INSUFFICIENT_CREDITS",
"message": "Partner credit balance exhausted.",
"details": null
}
}Test signed partner requests directly from this page.