API documentation
Two ways in. Paste a line of HTML and you're taking payments; call the API when you need the amount to come from your own system. Card details are entered inside CardPointe's frame and never touch your servers or ours.
Quick start
The shortest path: define a price in your dashboard, paste one line, done. No server code.
<script src="https://brijee.com/embed.js" data-price="PRICE_ID"></script>That renders a button. Clicking it opens the checkout in a pop-up over your page. The amount is read from the price in your account, so nobody can change it by editing the HTML.
Checkout API
When the amount comes from a cart or an invoice, your server creates the checkout and the browser only ever sees its id.
curl -X POST https://brijee.com/api/v1/checkout \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": "49.00",
"description": "Order 1042",
"successUrl": "https://yoursite.com/thanks"
}'
{ "ok": true,
"id": "3f9c…",
"url": "https://brijee.com/checkout/3f9c…" }Send the customer to url, or embed it in your own page:
<script src="https://brijee.com/embed.js" data-checkout="3f9c…"></script>amountrequiredDollars, e.g. "49.00".descriptionoptionalShown at checkout and on the receipt.successUrloptionalWhere to send them after paying.cancelUrloptionalWhere a cancel link goes.collectShippingoptionaltrue to ask the payer for a shipping address. It comes back in the payment.succeeded webhook and on your email.metadataoptionalYour own tag — any object up to 4KB. We never read it; it comes back on the webhook so you can match the payment to your customer.saveCardoptionaltrue to keep the card on file. The webhook then carries a savedCardId you can charge later.Keys live in your dashboard under Add Brijee to your site. Keep them on your server — a key in a web page can be read by anyone.
Subscriptions
Charge every month without writing a billing system. Define the price once; we do the rest.
In your dashboard, add a price and set it to monthly or yearly. Then it's the same one line as anything else:
<script src="https://brijee.com/embed.js" data-price="PRICE_ID"></script>When someone pays, the subscription starts. From then on we charge their card on schedule, email them three days before each renewal, and give them a link to cancel without logging in or contacting you. A declined card is retried; after three failures in a row we stop and tell you.
payment.succeededwebhookSent for the first payment and for every renewal.subscription.payment_failedwebhookTheir card was declined. Includes whether we've given up.Open access on the first, close it on the second — that's the whole integration. The renewal event carries renewal: true if you want to treat signup differently.
Bill it yourself
When the amount is different every month — seats, locations, a discount code — keep the card here and tell us what to take.
Subscriptions above are for a plan that is one fixed number. If yours is worked out from your own data, you already know the figure — so keep the amount where it lives and let us hold the card. First, take one payment with saveCard:
curl -X POST https://brijee.com/api/v1/checkout \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": "147.00",
"description": "September — 3 locations",
"saveCard": true,
"metadata": { "companyId": "co_8412" }
}'The payer sees that the card is being kept, and the payment.succeeded webhook comes back with a savedCardId alongside your metadata. Store it next to your customer. Every month after that is one call:
curl -X POST https://brijee.com/api/v1/charges \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"savedCardId": "b71e…",
"amount": "196.00",
"description": "October — 4 locations",
"metadata": { "companyId": "co_8412" },
"idempotencyKey": "co_8412-2026-10"
}'
{ "ok": true,
"id": "9d44…",
"retref": "208947053044",
"amount": "196.00",
"card": { "last4": "4242", "brand": "VISA" } }savedCardIdrequiredFrom the payment.succeeded webhook, or GET /api/v1/cards.amountrequiredDollars. Different every time is the point.descriptionoptionalShown on the receipt we email the customer.metadataoptionalYour tag again — echoed in the response and the webhook.idempotencyKeyoptionalSend one per bill. A retry with the same key returns the first answer instead of charging twice.scheduledoptionalDefaults to true — the customer was told when this would be taken. Send false for a one-off.A declined card answers 402 with the reason, so your billing run can retry or downgrade on its own schedule. We email the customer a receipt either way, and nothing here needs a page open — no customer is present.
Lost a webhook? GET /api/v1/cards lists the cards your customers have left on file, with ?email= to narrow it down.
One thing to know: billing your own way means the reminders and the cancel link are yours too. We send the receipt; telling a customer a charge is coming, and letting them stop it, happens in your product.
Subscriptions by API
Same as billing it yourself, except we keep the calendar. Tell us the figure whenever it changes; the charge date, the retries and the customer's cancel link are ours.
Use this when you're moving off a processor that ran the schedule for you. Create the subscription once against a card you already hold:
curl -X POST https://brijee.com/api/v1/subscriptions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"savedCardId": "b71e…",
"amount": "147.00",
"interval": "month",
"description": "3 locations",
"metadata": { "companyId": "co_8412" }
}'
{ "ok": true,
"id": "5c20…",
"status": "active",
"amount": "147.00",
"interval": "month",
"nextChargeAt": "2026-10-24T18:00:00.000Z",
"cancelUrl": "https://brijee.com/manage/7f31…" }savedCardIdrequiredFrom the payment.succeeded webhook, or GET /api/v1/cards.amountrequiredDollars. Change it any time — see below.intervalrequired"month" or "year".descriptionoptionalShown on the receipt and on the customer's cancel page.customerEmailoptionalDefaults to the email on the saved card.firstChargeAtoptionalMust be in the future. Defaults to one period from now.metadataoptionalYour tag — echoed on every webhook for this subscription.Creating one does not take a payment. The card got here by being stored during a checkout, and that checkout already charged them — billing again on signup day would charge twice. Put cancelUrlsomewhere the subscriber can find it; the card brands require a way to stop it that doesn't go through you.
When the figure changes — a location added, a discount applied — send the new one:
curl -X PATCH https://brijee.com/api/v1/subscriptions/5c20… \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "amount": "196.00", "description": "4 locations" }'It applies from the next charge onward. A period already billed is never reached back into. You can send amount, description or metadata — any of them, on their own.
Stopping it:
curl -X DELETE https://brijee.com/api/v1/subscriptions/5c20… \
-H "Authorization: Bearer YOUR_API_KEY"The record stays, so what was billed survives. We send no email — you cancelled it inside your own product, so you tell them in your own words. A subscriber who cancels through their own link does get one, because there nobody else would.
GET /api/v1/subscriptions lists yours, with ?status=active to narrow it. Each one carries nextChargeAt, lastChargeAt and failedAttempts so you can reconcile against your own records.
Refunds and cancels
Two different things, and the cheap one has a deadline.
Before the day's batch closes you can cancel a payment outright. It costs nothing and never reaches the customer's statement. After it closes, the only way back is a refund — which leaves the charge and the refund both showing, and carries the processing cost of each. So try the cancel first:
curl -X POST https://brijee.com/api/v1/voids \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "transactionId": "8a21…" }'
{ "ok": true,
"transactionId": "8a21…",
"canceled": "49.00",
"releasedHold": false,
"status": "voided" }releasedHold: truemeans this was a tip hold rather than a sale — nothing had been charged, and the amount is off the customer's available balance straight away. Worth saying so on your screen, because "payment canceled" would confuse someone who was never charged.
Too late, and the answer is refundInstead: true with the reason. We do not quietly refund for you — that moves real money on a call that asked for the free option. Refund it deliberately:
curl -X POST https://brijee.com/api/v1/refunds \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "transactionId": "8a21…", "amount": "20.00" }'transactionIdrequiredFrom the webhook, or GET /api/v1/transactions.amountoptionalLeave it out to give the whole sale back.reasonoptionalKept in your activity log. The customer never sees it.Both fire a webhook — payment.refunded and payment.voided — including when the refund is done from the Brijee dashboard instead of here, so your own system follows either.
One thing to know: a bank payment can't be given back until it has cleared, and once the customer's bank has sent it back itself there is nothing left to refund. Both answer 409 saying which it was.
Reading your payments
A webhook is delivered once and isn't retried. This is how you find the one you missed.
curl "https://brijee.com/api/v1/transactions?since=2026-09-01&limit=50" \
-H "Authorization: Bearer YOUR_API_KEY"
{ "ok": true,
"transactions": [
{ "id": "8a21…",
"status": "approved",
"amount": "49.00",
"tip": null,
"surcharge": null,
"method": "card",
"retref": "208947053044",
"customerEmail": "sam@example.com",
"savedCardId": "b71e…",
"metadata": { "companyId": "co_8412" },
"createdAt": "2026-09-24T02:14:09.000Z" } ],
"hasMore": false }statusoptionalapproved, refunded, voided, pending or returned.emailoptionalExact match on the payer's email.sinceoptionalA date, e.g. 2026-09-01.limitoptional1–200, 50 by default. hasMore tells you there are more.amount is the sale — the order and any tip. On a surcharging account the card fee is the separate surcharge field, and the cardholder paid the two added together. No card numbers come back, ever.
GET /api/v1/cards does the same for cards your customers have left on file, with ?email= to narrow it.
Webhooks
Set a URL in your dashboard and we post to it when a payment succeeds. Use this for anything that matters — a customer who closes the tab still gets their order.
POST https://yoursite.com/your-webhook
Brijee-Signature: t=1753651200,v1=9f86d081884c7d65…
{
"id": "evt_9f2c7a1b4d8e",
"type": "payment.succeeded",
"created": 1753651200,
"data": {
"checkoutId": "3f9c…",
"transactionId": "8a21…",
"amount": "49.00",
"method": "card",
"retref": "208947053044",
"businessName": "Kona Coffee Co.",
"savedCardId": "b71e…",
"metadata": { "companyId": "co_8412" }
}
}If your server doesn't answer with a 2xx we try again — six times over about a day and a half, spread out. That covers the ordinary case of a deploy restarting your server while a payment lands. Every attempt is listed in your dashboard with what came back, and you can send any of them again by hand once you've fixed the endpoint.
Because of that, the same event can reach you more than once. The idis the same every time, so record it and ignore one you've already acted on.
Check the signature before you trust the body — anyone can post to your URL, and this request is what tells you to hand over the goods. Sign "{t}.{raw body}" with your webhook secret and compare:
const [t, v1] = req.header("Brijee-Signature")
.split(",").map(function (p) { return p.split("=")[1]; });
const expected = crypto
.createHmac("sha256", process.env.BRIJEE_WEBHOOK_SECRET)
.update(t + "." + rawBody) // the raw body, before JSON.parse
.digest("hex");
if (v1 !== expected) return res.status(400).send("bad signature");If you only need to update the page the customer is looking at, the embed also fires a brijee:paid event in the browser.
Bank payments take days
A card is settled the moment it's approved. A bank payment (eCheck) is a request to the customer's bank, and that bank can send it back days or weeks later — not enough money, a closed account, or the customer saying they never authorized it. So a bank payment gets two events, and only the second one means the money is yours.
payment.pendingwebhookA bank payment started. Record the order — don't ship yet.payment.succeededwebhookThe money arrived. Cards get this immediately; bank payments get it when they clear.payment.returnedwebhookThe bank sent it back. Includes the reason and whether it's worth retrying.{
"type": "payment.returned",
"created": 1753996800,
"data": {
"transactionId": "8a21…",
"amount": "49.00",
"method": "echeck",
"retref": "208947053044",
"returnCode": "R01",
"reason": "There wasn't enough money in the account.",
"retryable": true
}
}Ship on payment.succeeded, never on payment.pending. If you only take cards, nothing changes for you — the first event you receive is still payment.succeeded.
Testing
Your account is in test mode until it's approved, so the whole flow can be built and tried before a real dollar can move.
Approval is the switch, and there isn't a setting for it. While you're waiting, every payment is simulated: the API answers exactly as it will later, a card handle comes back so subscriptions and saved-card charges can be tried too, and webhooks fire the same way. Nothing reaches the card networks and nothing settles. The receipt a customer would get comes to your own email instead, so you can see it without anyone else being emailed.
Test payments are kept apart from your takings — your dashboard says Test modewhile you're in it, and your checkout page says so too, so nobody pays you by accident before you can be paid. The day you're approved the simulation stops on its own and your totals start from zero.
Ready to start?
Apply for an account and you'll have keys in a few minutes.
Brijee — a service of Aloha Smart System · Honolulu, HI · (808) 650-3331 · support@brijee.com