Get started with Cosmo
This guide walks you through integrating your application with the Cosmo API — how to authenticate, work with partner programs, and issue points using the core endpoints.
Cosmo follows a simple flow: sign up, authenticate, retrieve your partnerships, and start issuing points. Sandbox mode lets you build and test safely before you connect to real partner programs in production.
- ✓Set up your organization and get credentials
- ✓Authenticate and make authorized requests
- ✓Load the programs available to your business
- ✓Validate destination users
- ✓Issue point transactions and apply campaign bonuses
- ✓Retrieve transactions and handle errors correctly
Account setup
There are two paths to getting set up with Cosmo, depending on your organization's needs.
Managed setup
White-glove onboardingFor enterprises that require guided onboarding, Cosmo manages account creation and configuration on your behalf:
- Provisioning your organization in Cosmo
- Assigning API credentials
- Enabling program partnerships
- Completing KYB and compliance checks
Credentials are shared once configuration is complete.
Self-service setup
Start in minutesPrefer to set up directly? Follow the onboarding flow in the Cosmo Portal:
- Sign up using your business email
- Enter your organization's legal name
- Complete the automated KYB process
- Organization and credentials are created automatically on approval
Sandbox mode is available immediately after completion.
Sign up
To begin using Cosmo, your team creates an organization and completes identity verification.
client_id / client_secret — become visible once your KYB is approved.Sandbox integration
Sandbox mode lets you build and test without affecting live partner programs. All Sandbox transactions are isolated from production systems.
- ✓Simulated transactions — no real balances are affected.
- ✓Error triggers — error responses can be triggered deterministically with specific
destinationUserIdvalues. - ✓Mirrors production — program configurations match production structure.
- ✓No real points — no partner systems are contacted.
https://api.cosmopoints.comhttps://api.sandbox.cosmopoints.comEvery endpoint path in this guide is appended to the base URL of the environment you're targeting.
client_id. What changes per environment is the client_secret: use your sandbox secret to get a sandbox access token, and your production secret for production.Sandbox matches production logic as closely as possible, so your integration behaves identically when you switch environments.
User flow
The complete flow your system follows to issue points through Cosmo. Each step maps to a specific API endpoint.
Authenticate
POST /oauth2/tokenYour backend sends grant_type: client_credentials together with your client_id and client_secret; Cosmo returns a short-lived JWT. Include Authorization: Bearer <token> in all requests. If the token expires, re-authenticate and retry.
Always authenticate against production — api.cosmopoints.com — even when you are working in sandbox. There is no separate sandbox auth endpoint, and the client_id is the same in both environments. The client_secret you send is what decides which environment the token is valid for: send your sandbox secret and this same endpoint returns a token for api.sandbox.cosmopoints.com; send your production secret and it returns a production token.
Retrieve partnerships
GET /programsFetch the partner programs available to your organization — for populating your UI, obtaining the programId needed for issuing, and reading each program's required fields. Cache the list, refresh periodically.
Issue points
POST /programs/{programId}/validate-memberCheck the destination user is valid before issuing — reduces failed transactions. See Validation behavior.
POST /transactionsCreates the transaction and issues points. Send basePoints — the number of points to issue — and optionally bonusPoints on top (used by campaigns). Send a unique referenceNumber with every request — generate and store it before you call. Resend the same value only when you never got a definitive answer (timeout, dropped connection or 504): that resend returns the original result and never issues points twice. A failed transaction cannot be retried — its reference number is consumed, so issue it again with a new referenceNumber. Response includes transaction ID, total points, reference number, and timestamp — store them for reconciliation.
Issue flow
Every issue call ends in one of four outcomes. This is the whole loop, from generating the reference number to the moment you can act on the result — success, definitive failure, and the resend that resolves a call whose answer you never saw.
- Generate a
referenceNumber, unique within your organization, and store it with the order before you call. - Call
POST /transactions. Keep the call open — Cosmo answers within 30 seconds. 201— or200on a resend: the points were issued. Store the transaction id,totalPointsandreferenceNumberfor reconciliation. Done.- Any
4xx, or any5xxother than504: the transfer failed and no points were issued. Release the customer's debit. A failed transaction is not retried: to issue again, go back to step 1 with a newreferenceNumber. The old one is consumed — resending it answers409witherrorCode: TRANSACTION_FAILED. 504, timeout, or no response: the outcome is unknown. Do not reverse anything. Wait briefly, then resend the exact same request — samereferenceNumber, same details — and go back to step 3. A resend never issues points twice; repeat until you get a definitive answer.
referenceNumber is only for finding out what happened to a call that never answered. A new referenceNumber is for issuing again after a failure. Never resend a failed transaction's reference number expecting a second attempt — see Idempotency and Timeouts & transaction states.Idempotency
Every transaction carries a referenceNumber that you generate. It is required, must be unique within your organization, and is how Cosmo recognises a resend — so generate and store it before you call, and reuse the exact same value only to resend a call that ended without a definitive answer. A failed transaction is never retried under its reference number: issue it again with a new one.
A reference number you haven’t used before — the transaction is created.
Same reference number, same transaction details — the original transaction is returned and nothing is issued a second time. Treat 200 as success, exactly like 201.
This reference number was already used for a transaction with different details. Use a new reference number, or resend the original details to get the original transaction back. See Timeouts & transaction states.
This reference number belongs to a transaction that failed. Send the corrected request under a new reference number.
errorCode: CONFLICT and the second returns errorCode: TRANSACTION_FAILED.programId, the points amount (basePoints and bonusPoints), and the destinationAccount fields. description is not included — retrying with an edited description still returns the original transaction rather than a conflict.If your organization is an aggregator, partnerCode is part of the uniqueness too — the same reference number sent under a different partnerCode is a different transaction, not a retry.Timeouts & transaction states
Issuing points involves a live call to the partner program. The API is synchronous — you get the outcome in the response. Two timeout budgets bound how long a call can take, and every transaction settles into one of two states.
Cosmo keeps each API request open for up to 30 seconds. Set your own HTTP-client timeout above that — 31 seconds or more — and don't abort a slow issue call early: it may still be waiting on the partner. A typical issue call takes around 200 ms, but this varies by partner program.
Within that window, the downstream call to the partner is capped at 24 seconds. If the partner doesn't answer in time, Cosmo still answers your request. Only a 504 or no response at all leaves the outcome unknown — follow the retry procedure below.
If your issue call times out, the connection drops, or you receive a 504, the outcome is unknown: the points may already have been issued to the customer's partner account. Reversing the debit on your side at that moment could leave the customer with both.
- Do not treat it as a failure and do not reverse anything. Keep the customer's debit on hold.
- Once the original call has ended, resend the exact same request: the same
referenceNumberand the same transfer details. A resend never issues points twice — if the original went through, you get it back with a200. - If the resend times out again, the transaction is still unresolved. Wait a short while and resend again. Repeat until you receive a definitive answer.
- Act on the definitive answer only: a
200or201means the transfer succeeded and nothing should be reversed; a definitive error means no points were issued and the debit can be released.
201 on the original call or 200 on a resend: the points were issued. Definitive.
The transfer failed and no points were issued. Definitive. Includes a rejection by the partner program (400) and a reference-number conflict (409).
Every 5xx other than 504: the transfer failed and no points were issued. Definitive. A 503 means the partner program could not process the credit; the transaction is recorded as FAILED and the reference number is consumed, so use a new one when you try again.
Timeout, or no response at all: the outcome is unknown. Not a failure. Resend the same request with the same reference number until you get a definitive answer.
The partner confirmed the request and points were issued. Terminal and successful.
The transfer failed definitively — the partner rejected the request or could not process it. Terminal and no points were issued. Safe to refund.
In very rare occurrences, we might provide you with some transactions as failed that were previously considered as success, due to partner discrepancies.
GET /transactions/{transactionId} returns the transaction with its status, COMPLETED or FAILED; a request the partner rejected is returned with status FAILED. A 404 means no transaction exists for that id or reference number. GET /transactions lists every transaction in a date range, whatever its outcome, each with the same status field — filter on it rather than assuming every listed transaction succeeded. Use idempotency — the same referenceNumber — to safely retry a request whose result you never saw, without issuing points twice.Key API endpoints
All paths below are relative to the base URL: https://api.cosmopoints.com for production, https://api.sandbox.cosmopoints.com for sandbox.
/programs/{programId}/validate-memberConfirm the destination user is valid before issuing. Request: destinationUserId, destinationUserEmail → Response: isValid. See Validation behavior.
/transactionsThe core transaction endpoint. Send basePoints — the exact number of points to issue — and, when a campaign applies, bonusPoints on top. A unique referenceNumber is required on every request — see Idempotency. Response: id, totalPoints, referenceNumber, date — store for reconciliation.
/transactions/{transactionId}Retrieve full details of any previous transaction, whatever its outcome — for reconciliation, support, and audit trails. The path parameter accepts either the transaction id or your own referenceNumber, which is resolved within your organization. Returns the transaction with its status, COMPLETED or FAILED. A 404 means no transaction exists for that id or reference number. GET /transactions lists every transaction in a date range with the same status field, including FAILED ones. See Timeouts & transaction states.
Validation behavior
Member validation depends on the selected partner program. Some partners provide a member validation API endpoint, while others do not. Cosmo handles /programs/{programId}/validate-member as follows:
Cosmo first checks the submitted details against the partner's validation rules. If the checks pass, Cosmo calls the partner's endpoint and returns the validation result.
Cosmo checks the submitted membership details against the validation rules provided by the partner. Cosmo returns isValid: true if the details satisfy these rules, and isValid: false otherwise. A true result confirms compliance with the validation rules only; it does not confirm that the membership exists or is eligible to receive points. For these partners, eligibility is checked when you submit the points issuance request: the partner validates the member details and either proceeds with issuance or rejects the request if the details are invalid.
Refer to Partner Validation Support and Rules for each partner's validation availability and applicable rules.
Campaigns
A campaign issues bonus points on top of the base points of every qualifying transaction, for a fixed date range. There are two funding models, and they decide who pays for the bonus — and what you send.
The earn partner — the destination program — funds the entire bonus. You are billed only for the base points; the bonus costs you nothing.
You do not send bonusPoints — Cosmo computes and credits the partner-funded bonus for you.
You (the client) and the earn partner each fund a share of the bonus. You are billed for the base points plus your share; the partner covers theirs.
You must send bonusPoints equal to your funded share: floor(basePoints × clientPercentage / 100).
Send only basePoints. Cosmo adds the partner-funded bonus itself. Sending bonusPoints is rejected with BONUS_POINTS_NOT_APPLICABLE.
Send basePoints and bonusPoints = floor(basePoints × clientPercentage / 100). Omit it and you get BONUS_POINTS_REQUIRED; send the wrong amount and you get INVALID_BONUS_POINTS.
Send only basePoints. With no running campaign, bonusPoints is rejected with BONUS_POINTS_NOT_APPLICABLE.
basePoints: 100 with bonusPoints: 20. The user is credited 140 — 100 base, plus your 20 and the partner's own funded share — while you are billed for 120 (base + your bonus) and the partner covers the rest.Transfer limits
A partnership can bound how many points a single transfer issues. Four optional limits apply — all expressed in the destination points being issued, and all measured against the basePoints only. Any campaign bonus is never counted toward a limit, so a bonus can never push a transfer over or under one. Two limits are set by the source program and two by the destination program. A transfer whose base points fall outside any set limit is rejected. A limit left empty is not enforced.
The lowest and highest number of destination points the source program allows a single transfer to issue.
minAllowedPoints — smallest issuable amount.maxAllowedPoints — largest issuable amount.
The lowest and highest number of destination points the destination program allows a single transfer to issue.
partnerMinAllowedPoints — smallest issuable amount.partnerMaxAllowedPoints — largest issuable amount.
MIN_SOURCE_NOT_REACHED or MIN_DESTINATION_NOT_REACHED; one above a maximum with MAX_SOURCE_EXCEEDED or MAX_DESTINATION_EXCEEDED. See Error handling for how to surface these to the user.Error handling
Sandbox supports deterministic error simulation via predefined destinationUserId values — no dedicated endpoint, triggered through the normal issue-points flow. Use it to test backend error handling, frontend messaging, and retry / reconciliation flows.
- Expired or invalid token
- Missing required fields
- Missing reference number
- Reference number already used for a different transaction
- Exceeding allowed transaction value limit
- Exceeding allowed transaction points limit
- Invalid program ID
- No partnership with the specified program
- Destination user rejected by the partner program
When the partner itself refuses the credit — an unknown, blocked or closed membership, for example — POST /transactions answers 400 with errorCode: BAD_REQUEST. message is the reason as classified by Cosmo, and data.partnerError is the partner's own error text, verbatim. It is present only when the partner supplied one and is worded differently per program, so display or log it rather than matching on it. The transaction is recorded as failed and no points are issued. The reference number stays used: send the corrected request under a new reference number, and expect 409 with errorCode TRANSACTION_FAILED if the failed one is resent. Sandbox error simulation returns the same shape.
{
"statusCode": 400,
"errorCode": "BAD_REQUEST",
"message": "Invalid destination user",
"data": {
"isValid": false,
"error": "Invalid destination user",
"partnerError": "Blocked User"
}
}Every 4xx, and every 5xx other than 504, is a definitive failure: no points were issued. Only a 504 or no response leaves the outcome unknown — see Timeouts & transaction states for the retry procedure.
All errors follow a structured response format for consistent handling.
Production integration
Once you've completed your sandbox testing, switching to production takes two changes: point your base URL from api.sandbox.cosmopoints.com to api.cosmopoints.com, and swap in your production client_secret (your client_id stays the same). No code changes — the API structure and endpoints are identical.
Production credentials can be requested once your KYB is approved and at least one partner program is enabled. Store them securely; never expose them client-side.
- Transactions interact with live partner systems
- User validation checks real member accounts
- All issued points are financially binding
- Error simulation is not available
- Confirm active partnerships
- Validate required destination fields per program
- Handle all error cases gracefully
- Verify reconciliation logic
- Test webhook handling (if used)
Going live
Going live means issuing real points to real accounts. Final checks before launch:
- ✓Production authentication flow is functioning
- ✓At least one real user validated via Validate Member
- ✓A small live transaction issued for internal testing
- ✓Reference numbers generated uniquely and stored before each call, with retries reusing the same value
- ✓Transaction retrievable via
GET /transactions/{transactionId} - ✓All transaction details stored in your internal system
After launch, monitor failed attempts, token-expiry errors, missing destination fields, traffic spikes, and reconciliation mismatches. All production transactions are permanent and billable — keep logs of validation and issue calls for downstream audits.
Multibrand widget
Lets users manage reward preferences for a Multibrand card — select and edit programs, decide how rewards are allocated, and update preferences over time. Your backend obtains a short-lived access token (server-side only — never expose clientId or clientSecret in the frontend) and your frontend launches the widget with it.
{
"clientId": "string",
"clientSecret": "string",
"userId": "string" // your unique user ID (can be opaque)
}
→ response: { "accessToken": "string" }<iframe src="https://multibrand.cosmopoints.com/?token=ACCESS_TOKEN" style="width: 100%; height: 600px; border: 0;" ></iframe>
https://multibrand.sandbox.cosmopoints.com/?token=ACCESS_TOKENWebhooks
Cosmo notifies your backend of important events, like completed transactions. Configure endpoints in the Cosmo Portal → Developers → Webhooks. Three authentication modes are supported:
Your configured username and password are included in the request — verify before processing.
Cosmo obtains a token from your token endpoint and includes it in webhook requests.
Every request is signed with your secret, delivered via x-cosmo-signature and x-cosmo-timestamp headers — verify cryptographically that requests are authentic and untampered.
Transaction webhook
Sent when a transaction completes. Process the transaction only after receiving it — typical uses: debit the user's balance, update your ledger, display results, trigger notifications.
{
"transactionId": "string",
"sourceProgramId": "string",
"destinationProgramId": "string",
"value": number,
"referenceNumber": "string",
"destinationAccount": {
"destinationUserEmail": "user@example.com",
"destinationUserId": "123456"
},
"timestamp": number,
"type": "string"
}Program connection webhook
Sent when a user links, edits, or removes program connections — update your internal preference model accordingly. type is LINKED | DELINKED | UPDATED.
Verifying signatures
Read the headers, take the raw JSON body exactly as sent, build ${timestamp}:${rawBody}, compute HMAC-SHA-256 with your secret, and compare with the signature — only process on match.
// verify-signature.js
const expected = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(`${timestamp}:${rawBody}`)
.digest('hex');
const valid = crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);- ✓Always verify signatures before processing any event
- ✓Use
timingSafeEqualto prevent timing attacks - ✓Log invalid signatures for security monitoring
- ✓Store multiple secret versions if you rotate keys
- ✓Don't parse or trust the body until after validation
- ✓Keep your endpoint publicly reachable over HTTPS