Getting started

Quick start

This guide takes you from zero to a live payment link in about 10 minutes. You'll make your first authenticated request, create a contact, create a payment intent, and get a URL your customer can pay on. Every request below is copy-paste ready — swap in your API key and tenant ID and run it.


Before you begin

You need two things:

Every request is authenticated with two headers:

Authorization: ApiKey your-api-key-here
X-TENANT-ID: your-tenant-id-here

Base URL

All requests go to https://api.qualyhq.com/v1. The API only uses GET and POST, accepts and returns JSON, and works on one object per request. See Authorization.


Step 1: Make your first request

Before writing any integration code, confirm your credentials work. This GET returns the user tied to your API key:

curl https://api.qualyhq.com/v1/users/user \
  -H 'Authorization: ApiKey your-api-key-here' \
  -H 'X-TENANT-ID: your-tenant-id-here'

A successful response looks like this:

{
  "data": {
    "_id": "6606ab7bfb9085579f1b5769",
    "type": "api-key",
    "email": "you@example.com"
  }
}

Every response is wrapped in `data`

Qualy wraps every successful response in a { "data": ... } envelope. Your actual object is always under data — so it's response.data._id, not response._id. List endpoints add hasMore and count next to data for pagination. Keep this in mind for every example below.

If you get a 401, double-check the Authorization header format (ApiKey prefix, then the key). If you get a 400 Invalid Tenant ID, check your X-TENANT-ID.


Step 2: Set up your headers

From here we'll use JavaScript. Define your headers once and reuse them:

const API = 'https://api.qualyhq.com/v1';

const headers = {
  'Content-Type': 'application/json',
  'Authorization': 'ApiKey your-api-key-here',
  'X-TENANT-ID': 'your-tenant-id-here',
};

See Authorization for details on API keys, tenant IDs, and user types.


Step 3: Create a contact

A contact is the person who will pay. You create them once and reuse them across payments. A contact requires an email and a profile object:

curl -X POST https://api.qualyhq.com/v1/contacts/create \
  -H 'Authorization: ApiKey your-api-key-here' \
  -H 'X-TENANT-ID: your-tenant-id-here' \
  -H 'Content-Type: application/json' \
  -d '{
    "email": "john.doe@example.com",
    "profile": { "firstName": "John", "lastName": "Doe", "phone": "+61412345678" }
  }'

See: Contacts API reference

You can skip IDs with smart references

Anywhere the API expects a reference (like the payment intent's contact field), you can pass a natural identifier such as the contact's email instead of its ObjectId. See Smart references.


Step 4: Create a payment intent

A payment intent defines what's owed: the contact, currency, and line items. Amounts are in minor units (cents), so 100000 is $1,000.00 AUD.

curl -X POST https://api.qualyhq.com/v1/payment-intents/create \
  -H 'Authorization: ApiKey your-api-key-here' \
  -H 'X-TENANT-ID: your-tenant-id-here' \
  -H 'Content-Type: application/json' \
  -d '{
    "intentType": "ad-hoc",
    "contact": "john.doe@example.com",
    "currency": "AUD",
    "dueAt": "2026-08-10T14:26:07.369Z",
    "items": [{ "name": "Tuition fee", "category": "Course", "amount": 100000 }]
  }'

Which link do I send?

The response's links.long is always present and is the canonical payment URL. links.short is a shortened, share-friendly version that's added when available — prefer it for emails, SMS, and WhatsApp, and fall back to links.long.

Use intentType: 'ad-hoc' for one-off charges. For payment plans and installments, see Creating payment intents and Creating orders.


Step 5: Collect the payment

Send the payment link from Step 4 to your customer. When they open it, Qualy's hosted payment page handles method selection, payer verification, and processing:

const paymentUrl = paymentIntent.links.short ?? paymentIntent.links.long;
// Redirect the customer here, or send it by email/SMS/WhatsApp.

This hosted flow is the fastest way to start and is required for card payments. If you need to render non-card methods (PIX, Boleto, PayID, bank transfers) inside your own UI, see Collecting payments.


Step 6: Know when you get paid

Poll the payment intent to check its status:

const statusRes = await fetch(`${API}/payment-intents/${paymentIntent._id}`, {
  method: 'GET',
  headers,
});

const { data: latest } = await statusRes.json();
console.log('Status:', latest.status); // "due" → "paid-full" (or "paid-partial")

For production, don't poll — set up webhooks so Qualy notifies your server the moment a payment succeeds or fails.


Complete example

The whole flow in one script:

const API = 'https://api.qualyhq.com/v1';
const headers = {
  'Content-Type': 'application/json',
  'Authorization': 'ApiKey your-api-key-here',
  'X-TENANT-ID': 'your-tenant-id-here',
};

// 1. Create a contact
const { data: contact } = await fetch(`${API}/contacts/create`, {
  method: 'POST', headers,
  body: JSON.stringify({
    email: 'john.doe@example.com',
    profile: { firstName: 'John', lastName: 'Doe', phone: '+61412345678' },
  }),
}).then((r) => r.json());

// 2. Create a payment intent
const { data: paymentIntent } = await fetch(`${API}/payment-intents/create`, {
  method: 'POST', headers,
  body: JSON.stringify({
    intentType: 'ad-hoc',
    contact: contact._id,
    currency: 'AUD',
    dueAt: '2026-08-10T14:26:07.369Z',
    items: [{ name: 'Tuition fee', category: 'Course', amount: 100000 }],
  }),
}).then((r) => r.json());

// 3. Send this link to your customer
console.log('Pay here:', paymentIntent.links.short ?? paymentIntent.links.long);

// 4. Check the status later
const { data: latest } = await fetch(
  `${API}/payment-intents/${paymentIntent._id}`,
  { headers },
).then((r) => r.json());
console.log('Status:', latest.status);

Next steps


Previous
Getting started