Skip to content

Quickstart

One HTTP call gets you a hosted checkout URL that collects a donation — PSE or a card in Colombia, PayPal in Mexico, a card from anywhere else. This page is that call, plus how to know it worked.

You need two things: an API key with the checkout_links:write permission, and the key of the vaki you are collecting for — the slug in its public URL. If https://vaki.co/clinicalaliga is the cause, the key is clinicalaliga. If you do not have a cause yet, create one first.

Never commit it, never put it in a query string, never ship it to a browser.

Terminal window
export VAKI_API_KEY="…the key Vaki issued you…"
Terminal window
curl https://api.vaki.cohttps://public-api-staging.vaki.co/v1/checkout_links \
-H "Authorization: Bearer $VAKI_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: quickstart-$(date +%s)" \
-d '{
"vaki": "clinicalaliga",
"amount": 50000,
"currency": "COP",
"email": "donante@example.com",
"external_reference": "DON_59LPC74YV",
"callback_url": "https://tu-sitio.example.com/api/vaki/callback"
}'

201 Created:

{
"id": "chl_01J9Z4M2K7QF3B",
"object": "checkout_link",
"url": "https://vaki.co/checkout/clinicalaliga?value=50000&currency=COP&ref=chl_01J9Z4M2K7QF3B",
"status": "open",
"amount": 50000,
"currency": "COP",
"fee_percent": 5,
"external_reference": "DON_59LPC74YV",
"expires_at": "2026-08-18T14:00:00Z",
"created_at": "2026-08-17T14:00:00Z"
}

Only vaki, amount and currency are required. Everything else is convenience: email prefills the donor’s address, external_reference is your own id coming back to you, callback_url is stored for when webhook delivery ships, and success_url / cancel_url control where the donor lands afterwards.

currency must be the currency the cause itself collects in — anything else is a 422 with currency_not_supported, because we will not pick an exchange rate for you. Read it from GET /v1/vakis/{key} (finance_data.currency) rather than assuming COP; see a cause collects in exactly one currency.

Terminal window
open "https://vaki.co/checkout/clinicalaliga?value=50000&currency=COP&ref=chl_01J9Z4M2K7QF3B"

That is the whole integration. Redirect, do not embed.

Poll the link. status moves from open to completed once the payment is approved.

Terminal window
curl https://api.vaki.cohttps://public-api-staging.vaki.co/v1/checkout_links/chl_01J9Z4M2K7QF3B \
-H "Authorization: Bearer $VAKI_API_KEY"
{
"id": "chl_01J9Z4M2K7QF3B",
"object": "checkout_link",
"status": "completed",
"amount": 50000,
"currency": "COP",
"fee_percent": 5,
"external_reference": "DON_59LPC74YV",
"expires_at": "2026-08-18T14:00:00Z",
"created_at": "2026-08-17T14:00:00Z"
}

status is one of:

StatusMeaning
openMinted, not paid yet. Still usable.
completedThe donation was approved. Terminal.
expiredPassed expires_at without being paid. Mint a new one.
cancelledVoided before payment. Terminal.

A sensible polling shape: poll when the donor returns to your success_url, and then once a minute for a few minutes, backing off. Do not poll a link that has reached a terminal status, and do not poll every link you have ever minted on a cron — that is what the rate limit is there to stop.

const res = await fetch('https://api.vaki.cohttps://public-api-staging.vaki.co/v1/checkout_links', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.VAKI_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': `donation-${orderId}`
},
body: JSON.stringify({
vaki: 'clinicalaliga',
amount: 50000,
currency: 'COP',
email: 'donante@example.com',
external_reference: orderId
})
});
if (!res.ok) {
// Problem documents are application/problem+json — branch on `code`.
const problem = await res.json();
throw new Error(`${problem.code}: ${problem.detail ?? problem.title}`);
}
const link = await res.json();
// Persist link.id against your own order before redirecting.
return link.url;
import os
import requests
res = requests.post(
"https://api.vaki.cohttps://public-api-staging.vaki.co/v1/checkout_links",
headers={
"Authorization": f"Bearer {os.environ['VAKI_API_KEY']}",
"Content-Type": "application/json",
"Idempotency-Key": f"donation-{order_id}",
},
json={
"vaki": "clinicalaliga",
"amount": 50000,
"currency": "COP",
"email": "donante@example.com",
"external_reference": order_id,
},
timeout=15,
)
if res.status_code != 201:
problem = res.json()
raise RuntimeError(f"{problem['code']}: {problem.get('detail', problem['title'])}")
link = res.json()
checkout_url = link["url"]

If the cause does not exist yet, create it. The owner must already be a registered Vaki account — pass their email and we resolve it.

Terminal window
curl https://api.vaki.cohttps://public-api-staging.vaki.co/v1/vakis \
-H "Authorization: Bearer $VAKI_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: vaki-N_2QFBQFJTY" \
-d '{
"title": "Ayuda para el Hospital San Jorge",
"description": "Dotación de la sala de urgencias del Hospital San Jorge.",
"goal": { "amount": 50000000, "currency": "COP" },
"category": "no-profit",
"country": "CO",
"owner": { "name": "Nombre del responsable", "email": "responsable@example.org" },
"external_reference": "N_2QFBQFJTY",
"state": "draft"
}'

201 Created returns the vaki, including the key you pass to POST /v1/checkout_links:

{
"key": "ayuda-hospital-san-jorge",
"slug": "ayuda-hospital-san-jorge",
"url": "https://vaki.co/ayuda-hospital-san-jorge",
"title": "Ayuda para el Hospital San Jorge",
"state": "draft",
"goal": { "amount": 50000000, "currency": "COP" },
"finance_data": { "currency": "COP", "fee_percent": 5 },
"owners": [{ "name": "Nombre del responsable", "email": "responsable@example.org" }],
"created_at": "2026-08-17T13:58:11Z"
}

A cause created in draft is not publicly visible. Publish it from the Vaki dashboard, or pass "state": "published" if you want it live immediately.

If the owner email does not resolve to an existing Vaki account you get a 422 with code: "owner_not_found". Creating an account on the owner’s behalf is coming soon.