Quick Start
Welcome to Zelta Pay API! This guide will help you create your first payment link and understand the basics of our API.
Prerequisites
Section titled “Prerequisites”Before you begin, make sure you have:
- A Zelta Pay account
- An API key (obtain from your dashboard)
- Basic knowledge of HTTP requests
- A development environment ready
Step 1: Get Your API Key
Section titled “Step 1: Get Your API Key”- Log in to your Zelta Pay Dashboard
- Open API Key in the sidebar (shown to the admin and owner roles only)
- In the Nueva API key card, type a descriptive alias (e.g., “My App Integration”) and click Crear. The button stays disabled until the account has an active payment provider
- Click Ver llave on the new row, then Copiar, and store the key securely
Step 2: Make Your First API Call
Section titled “Step 2: Make Your First API Call”Let’s test your API key by listing your existing payment links:
cURL Example
Section titled “cURL Example”curl -X GET "https://api-pay.zelta.dev/v1/payment-links" \ -H "X-API-Key: your-api-key-here" \ -H "Content-Type: application/json"Node.js Example
Section titled “Node.js Example”const response = await fetch('https://api-pay.zelta.dev/v1/payment-links', { method: 'GET', headers: { 'X-API-Key': 'your-api-key-here', 'Content-Type': 'application/json' }});
const result = await response.json();console.log(result);Cloudflare Workers Example
Section titled “Cloudflare Workers Example”export default { async fetch(request, env, ctx) { const response = await fetch('https://api-pay.zelta.dev/v1/payment-links', { method: 'GET', headers: { 'X-API-Key': env.ZELTA_API_KEY, 'Content-Type': 'application/json' } });
const result = await response.json(); return new Response(JSON.stringify(result), { headers: { 'Content-Type': 'application/json' } }); }};Hono with Cloudflare Workers Example
Section titled “Hono with Cloudflare Workers Example”import { Hono } from 'hono';import { cors } from 'hono/cors';
type Bindings = { ZELTA_API_KEY: string;};
const app = new Hono<{ Bindings: Bindings }>();
// Enable CORSapp.use('*', cors());
app.get('/payment-links', async (c) => { const response = await fetch('https://api-pay.zelta.dev/v1/payment-links', { method: 'GET', headers: { 'X-API-Key': c.env.ZELTA_API_KEY, 'Content-Type': 'application/json' } });
const result = await response.json(); return c.json(result);});
export default app;Python Example
Section titled “Python Example”import requests
response = requests.get( 'https://api-pay.zelta.dev/v1/payment-links', headers={ 'X-API-Key': 'your-api-key-here', 'Content-Type': 'application/json' })
result = response.json()print(result)Step 3: Create Your First Payment Link
Section titled “Step 3: Create Your First Payment Link”Now let’s create a payment link:
cURL Example
Section titled “cURL Example”curl -X POST "https://api-pay.zelta.dev/v1/payment-links" \ -H "X-API-Key: your-api-key-here" \ -H "Content-Type: application/json" \ -d '{ "concept": "Test Payment", "amount": 100, "customerName": "John Doe", "isTest": true }'Node.js Example
Section titled “Node.js Example”const response = await fetch('https://api-pay.zelta.dev/v1/payment-links', { method: 'POST', headers: { 'X-API-Key': 'your-api-key-here', 'Content-Type': 'application/json' }, body: JSON.stringify({ concept: 'Test Payment', amount: 100, customerName: 'John Doe', isTest: true })});
const result = await response.json();console.log(result);Cloudflare Workers Example
Section titled “Cloudflare Workers Example”export default { async fetch(request, env, ctx) { const response = await fetch('https://api-pay.zelta.dev/v1/payment-links', { method: 'POST', headers: { 'X-API-Key': env.ZELTA_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ concept: 'Test Payment', amount: 100, customerName: 'John Doe', isTest: true }) });
const result = await response.json(); return new Response(JSON.stringify(result), { headers: { 'Content-Type': 'application/json' } }); }};Hono with Cloudflare Workers Example
Section titled “Hono with Cloudflare Workers Example”import { Hono } from 'hono';import { cors } from 'hono/cors';
type Bindings = { ZELTA_API_KEY: string;};
const app = new Hono<{ Bindings: Bindings }>();
// Enable CORSapp.use('*', cors());
app.post('/payment-links', async (c) => { const body = await c.req.json();
const response = await fetch('https://api-pay.zelta.dev/v1/payment-links', { method: 'POST', headers: { 'X-API-Key': c.env.ZELTA_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ concept: 'Test Payment', amount: 100, customerName: 'John Doe', isTest: true }) });
const result = await response.json(); return c.json(result);});
export default app;Python Example
Section titled “Python Example”import requests
response = requests.post( 'https://api-pay.zelta.dev/v1/payment-links', headers={ 'X-API-Key': 'your-api-key-here', 'Content-Type': 'application/json' }, json={ 'concept': 'Test Payment', 'amount': 100, 'customerName': 'John Doe', 'isTest': True })
result = response.json()print(result)Step 4: Understanding the Response
Section titled “Step 4: Understanding the Response”A successful response will look like this:
{ "success": true, "data": { "paymentLink": { "id": "7312345678901234567", "paymentLinkUrl": "https://pay.zelta.dev/Xk3v9Q_aB7mN2pL8rT4wYz1c", "customerName": "John Doe", "concept": "Test Payment", "amount": 100, "status": "pending", "createdAt": "2024-01-15T10:30:00.000Z", "expiresAt": "2024-01-18T10:30:00.000Z", "isTest": true } }, "message": "Payment link created successfully", "timestamp": "2024-01-15T10:30:00.000Z"}Key Response Fields
Section titled “Key Response Fields”paymentLinkUrl: The URL your customer will use to pay. Its last path segment is the link’shashUrl, which you pass toGET /v1/payment-links/{hashUrl}and to the cancel endpointid: Unique identifier for the payment link (a numeric string)status: Current status (always “pending” for new links)expiresAt: When the link expires: 3 days after creation by default. SendexpiresInDays(1 to 60) to choose another lifetime; see Create Payment Link
Step 5: Handle the Payment
Section titled “Step 5: Handle the Payment”Once you have the payment link URL, you can:
- Send it to your customer via email, SMS, or display it on your website
- Redirect them to the payment portal
- Monitor the status by calling the API or setting up webhooks
Redirect Example
Section titled “Redirect Example”// Redirect customer to payment pagewindow.location.href = result.data.paymentLink.paymentLinkUrl;Step 6: Set Up Webhooks (Optional)
Section titled “Step 6: Set Up Webhooks (Optional)”To receive real-time notifications when payments are completed:
- Open Webhooks in the dashboard sidebar (shown to the admin and owner roles only)
- In the Nuevo webhook card, enter your webhook URL (must be a public HTTPS URL) and click Crear. An account has a single webhook, and creating it requires an active payment provider
- Click Enviar ping to send a signed
webhook.pingto your endpoint
There is no event selection: the webhook receives every event for the account’s payment links (payment.success, payment.failed, receipt.created…). Ignore the types you don’t handle.
Webhook Example
Section titled “Webhook Example”// Node.js with Expressapp.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => { const signature = req.headers['zeltapay-signature']; const timestamp = req.headers['zeltapay-timestamp'];
// Verify signature (important for security) if (!verifySignature(req.body.toString(), timestamp, signature)) { return res.status(401).json({ error: 'Invalid signature' }); }
const payload = JSON.parse(req.body);
if (payload.type === 'payment.success') { console.log('Payment completed:', payload.transaction.amount); // Update your database, send confirmation email, etc. }
res.status(200).json({ received: true });});Cloudflare Workers Webhook Example
Section titled “Cloudflare Workers Webhook Example”export default { async fetch(request, env, ctx) { if (request.method !== 'POST') { return new Response('Method not allowed', { status: 405 }); }
const url = new URL(request.url); if (url.pathname !== '/webhook') { return new Response('Not found', { status: 404 }); }
try { const signature = request.headers.get('zeltapay-signature'); const timestamp = request.headers.get('zeltapay-timestamp');
// Get raw body const rawBody = await request.text();
// Verify signature (important for security) if (!await verifySignature(rawBody, timestamp, signature, env.WEBHOOK_SECRET)) { return new Response(JSON.stringify({ error: 'Invalid signature' }), { status: 401, headers: { 'Content-Type': 'application/json' } }); }
const payload = JSON.parse(rawBody);
if (payload.type === 'payment.success') { console.log('Payment completed:', payload.transaction.amount); // Update your database, send confirmation email, etc. }
return new Response(JSON.stringify({ received: true }), { status: 200, headers: { 'Content-Type': 'application/json' } });
} catch (error) { console.error('Webhook processing error:', error); return new Response(JSON.stringify({ error: 'Processing failed' }), { status: 500, headers: { 'Content-Type': 'application/json' } }); } }};
async function verifySignature(rawBody, timestamp, signature, secret) { // Implementation details in signature verification guide return true; // Placeholder}Hono with Cloudflare Workers Webhook Example
Section titled “Hono with Cloudflare Workers Webhook Example”import { Hono } from 'hono';import { cors } from 'hono/cors';
type Bindings = { WEBHOOK_SECRET: string;};
const app = new Hono<{ Bindings: Bindings }>();
// Enable CORSapp.use('*', cors());
app.post('/webhook', async (c) => { try { const signature = c.req.header('zeltapay-signature'); const timestamp = c.req.header('zeltapay-timestamp');
if (!signature || !timestamp) { return c.json({ error: 'Missing required headers' }, 400); }
// Get raw body const rawBody = await c.req.text();
// Verify signature (important for security) if (!await verifySignature(rawBody, timestamp, signature, c.env.WEBHOOK_SECRET)) { return c.json({ error: 'Invalid signature' }, 401); }
const payload = JSON.parse(rawBody);
if (payload.type === 'payment.success') { console.log('Payment completed:', payload.transaction.amount); // Update your database, send confirmation email, etc. }
return c.json({ received: true });
} catch (error) { console.error('Webhook processing error:', error); return c.json({ error: 'Processing failed' }, 500); }});
async function verifySignature(rawBody: string, timestamp: string, signature: string, secret: string): Promise<boolean> { // Implementation details in signature verification guide return true; // Placeholder}
export default app;Next Steps
Section titled “Next Steps”Now that you’ve created your first payment link, explore these guides:
- Authentication - Learn about API keys and rate limiting
- Creating Payment Links - Advanced options and validation
- Webhooks - Real-time payment notifications
- API Reference - Complete endpoint documentation
Common Issues
Section titled “Common Issues”401 Unauthorized
Section titled “401 Unauthorized”- The
X-API-Keyheader is missing: ensure it is included
404 Not Found (ERR_ACCOUNT_API_KEY_NOT_FOUND)
Section titled “404 Not Found (ERR_ACCOUNT_API_KEY_NOT_FOUND)”- The API key is wrong, was regenerated or was deleted. Keys have no active/inactive state
- Copy the current key from API Key → Ver llave in the dashboard
400 Bad Request
Section titled “400 Bad Request”- Check that all required fields are provided (and sent with
Content-Type: application/json) - Ensure the amount is in cents (e.g., 100 = $1.00); the minimum is 100
- Verify the JSON format is correct
429 Too Many Requests
Section titled “429 Too Many Requests”- You’ve exceeded the rate limit (60 requests per 60-second window per API key)
- Wait the number of seconds in the
Retry-Afterheader before making more requests (the body is plain text, not JSON) - Consider implementing request queuing
Support
Section titled “Support”If you need help:
- Documentation: Browse our guides and API reference
- Support: Contact us at support@zelta.dev