Chapter 1The flow
- Your server creates the session
POST /v1/checkout/sessionswith your secret key. - You redirect the investor to
session.urlIt is their investment page, with amount and email prefilled. - The investor signs in, accepts and signsLike any investment.
- They return to your
success_urlWith?sesion=cs_…&inversion=…. - Your webhook fires
checkout.session.completedandinvestment.confirmed, with yourreference.
Chapter 2Creating the session
curl https://xvxhouuprjsmpfiwxkzz.supabase.co/functions/v1/api/v1/checkout/sessions \
-H "Authorization: Bearer $KROWS_SECRET_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-1234" \
-d '{
"amount": 250,
"email": "investor@email.com",
"reference": "order-1234",
"metadata": { "campaign": "autumn" },
"success_url": "https://yoursite.com/thanks",
"cancel_url": "https://yoursite.com/invest"
}'// Express: POST /invest → creates the session and redirects the investor
app.post('/invest', async (req, res) => {
const r = await fetch('https://xvxhouuprjsmpfiwxkzz.supabase.co/functions/v1/api/v1/checkout/sessions', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KROWS_SECRET_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': req.body.orderId,
},
body: JSON.stringify({
suggested_amount: 100, // the investor can change it
reference: req.body.orderId, // comes back in the investment and webhooks
success_url: 'https://yoursite.com/thanks',
cancel_url: 'https://yoursite.com/invest',
}),
});
const session = await r.json();
if (!r.ok) return res.status(400).json(session.error);
res.redirect(303, session.url);
});import os, requests
r = requests.post(
"https://xvxhouuprjsmpfiwxkzz.supabase.co/functions/v1/api/v1/checkout/sessions",
headers={
"Authorization": f"Bearer {os.environ['KROWS_SECRET_KEY']}",
"Idempotency-Key": "order-1234",
},
json={"amount": 250, "reference": "order-1234",
"success_url": "https://yoursite.com/thanks"},
)
session = r.json()
# redirect the investor to session["url"]{
"object": "checkout.session",
"id": "cs_live_8Kd2…",
"mode": "live",
"status": "open",
"url": "https://checkout.krows.app/aurora-solar?s=cs_live_8Kd2…",
"amount": 250,
"amount_fixed": true,
"email": "investor@email.com",
"reference": "order-1234",
"metadata": { "campaign": "autumn" },
"success_url": "https://yoursite.com/thanks",
"cancel_url": "https://yoursite.com/invest",
"investment": null,
"created": "2026-10-17T10:00:00.000Z",
"expires_at": "2026-10-18T10:00:00.000Z",
"completed_at": null
}| Field | Description |
|---|---|
| amountnumber, optional | Fixed amount in USDC (at least 1, up to 2 decimals, not above the goal). The investor cannot change it. |
| suggested_amountnumber, optional | Proposed amount the investor can change. Use one or the other, not both. |
| emailstring, optional | Prefills the investor’s email on the page. |
| referencestring, optional | Your identifier (order, user…), up to 200 characters. Returned in the investment and webhooks. |
| metadataobject, optional | Up to 20 keys and 2,000 characters. Returned as-is, untranslated. |
| success_urlURL, optional | Where the investor goes when done. We append sesion and inversion as query parameters. |
| cancel_urlURL, optional | “Go back” button if the session expired or was already paid. |
| Idempotency-Keyheader, recommended | Up to 100 characters. Repeating the request with the same key returns the same session. |
Chapter 3Statuses and expiry
| open | Can be paid. Expires after 24 hours. |
| complete | Invested. investment has the id and completed_at the date. |
| expired | Expired, or you closed it with POST /v1/checkout/sessions/:id/expire. |
If the investor opens a completed or expired session, they see “This payment is already completed” or “This payment link has expired” and a button to your cancel_url.
Chapter 4In a window, without leaving your site
If you open the investment page in a window with ?v=1, when it finishes it notifies your page via postMessage (public investment data only). Always check the origin:
// Open the investment page in a window and hear back when it completes
const w = window.open('https://checkout.krows.app/aurora-solar?v=1', 'krows', 'width=480,height=760');
window.addEventListener('message', (e) => {
if (e.origin !== 'https://checkout.krows.app') return;
if (e.data?.tipo === 'krows:inversion') {
// { tipo, slug, modo, id, importe, tokens, explorador } — public data only
console.log('New investment', e.data.id, e.data.importe);
}
});To grant something valuable (access, product), trust the signed webhook, not the browser message.
Was this helpful?
Thanks. We will use it to improve.