# Quick start

> Go from zero to your first collected payment with the Qualy API in about 10 minutes.

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:

- **An API key** — create one in the [Dashboard](https://dashboard.qualyhq.com) under **Settings → API keys & webhooks**. See [Creating your API keys](/docs/api-keys.md).
- **Your tenant ID** — find it alongside your API keys. See [Multi-tenancy](/docs/tenants.md).

Every request is authenticated with two headers:

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

> **Note — 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](/docs/authentication.md).

---

## 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**

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

**JavaScript**

```javascript
const res = await fetch('https://api.qualyhq.com/v1/users/user', {
  headers: {
    'Authorization': 'ApiKey your-api-key-here',
    'X-TENANT-ID': 'your-tenant-id-here',
  },
})
const { data: user } = await res.json()
console.log(user._id)
```

**PHP (Laravel)**

```php
use Illuminate\Support\Facades\Http;

$response = Http::withHeaders([
    'Authorization' => 'ApiKey your-api-key-here',
    'X-TENANT-ID' => 'your-tenant-id-here',
])->get('https://api.qualyhq.com/v1/users/user');

$user = $response->json('data'); // unwrap the `data` envelope
echo $user['_id'];
```

A successful response looks like this:

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

> **Warning — 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:

```javascript
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](/docs/authentication.md) 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**

```bash
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" }
  }'
```

**JavaScript**

```javascript
const contactRes = await fetch(`${API}/contacts/create`, {
  method: 'POST',
  headers,
  body: JSON.stringify({
    email: 'john.doe@example.com',
    profile: {
      firstName: 'John',
      lastName: 'Doe',
      phone: '+61412345678',
    },
  }),
});

// Unwrap the `data` envelope
const { data: contact } = await contactRes.json();
console.log('Contact ID:', contact._id); // e.g. "6a4e0cec9a0ac53047a253f3"
```

**PHP (Laravel)**

```php
use Illuminate\Support\Facades\Http;

$contact = Http::withHeaders([
    'Authorization' => 'ApiKey your-api-key-here',
    'X-TENANT-ID' => 'your-tenant-id-here',
])->post('https://api.qualyhq.com/v1/contacts/create', [
    'email' => 'john.doe@example.com',
    'profile' => [
        'firstName' => 'John',
        'lastName' => 'Doe',
        'phone' => '+61412345678',
    ],
])->json('data');

echo $contact['_id']; // e.g. "6a4e0cec9a0ac53047a253f3"
```

See: [Contacts API reference](https://v1-spec.qualyhq.com/#post-/v1/contacts/create)

> **Note — 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](/docs/smart-references.md).

---

## 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**

```bash
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 }]
  }'
```

**JavaScript**

```javascript
const intentRes = await fetch(`${API}/payment-intents/create`, {
  method: 'POST',
  headers,
  body: JSON.stringify({
    intentType: 'ad-hoc',
    contact: contact._id,        // or 'john.doe@example.com' — see Smart references
    currency: 'AUD',
    dueAt: '2026-08-10T14:26:07.369Z',
    items: [
      { name: 'Tuition fee', category: 'Course', amount: 100000 },
    ],
  }),
});

const { data: paymentIntent } = await intentRes.json();
console.log('Payment intent ID:', paymentIntent._id);
console.log('Payment link:', paymentIntent.links.short ?? paymentIntent.links.long);
```

**PHP (Laravel)**

```php
use Illuminate\Support\Facades\Http;

$paymentIntent = Http::withHeaders([
    'Authorization' => 'ApiKey your-api-key-here',
    'X-TENANT-ID' => 'your-tenant-id-here',
])->post('https://api.qualyhq.com/v1/payment-intents/create', [
    'intentType' => 'ad-hoc',
    'contact' => $contact['_id'], // or 'john.doe@example.com' — see Smart references
    'currency' => 'AUD',
    'dueAt' => '2026-08-10T14:26:07.369Z',
    'items' => [
        ['name' => 'Tuition fee', 'category' => 'Course', 'amount' => 100000],
    ],
])->json('data');

echo $paymentIntent['links']['short'] ?? $paymentIntent['links']['long'];
```

> **Note — 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](/docs/guides/creating-payment-intents.md) and [Creating orders](/docs/guides/creating-orders.md).

---

## 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:

```javascript
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](/docs/guides/collecting-payments.md).

---

## Step 6: Know when you get paid

Poll the payment intent to check its status:

```javascript
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](/docs/webhooks.md) so Qualy notifies your server the moment a payment succeeds or fails.

---

## Complete example

The whole flow in one script:

**JavaScript**

```javascript
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);
```

**PHP (Laravel)**

```php
use Illuminate\Support\Facades\Http;

$api = Http::baseUrl('https://api.qualyhq.com/v1')->withHeaders([
    'Authorization' => 'ApiKey your-api-key-here',
    'X-TENANT-ID' => 'your-tenant-id-here',
]);

// 1. Create a contact
$contact = $api->post('/contacts/create', [
    'email' => 'john.doe@example.com',
    'profile' => ['firstName' => 'John', 'lastName' => 'Doe', 'phone' => '+61412345678'],
])->json('data');

// 2. Create a payment intent
$paymentIntent = $api->post('/payment-intents/create', [
    'intentType' => 'ad-hoc',
    'contact' => $contact['_id'],
    'currency' => 'AUD',
    'dueAt' => '2026-08-10T14:26:07.369Z',
    'items' => [['name' => 'Tuition fee', 'category' => 'Course', 'amount' => 100000]],
])->json('data');

// 3. Send this link to your customer
echo $paymentIntent['links']['short'] ?? $paymentIntent['links']['long'];

// 4. Check the status later
$latest = $api->get("/payment-intents/{$paymentIntent['_id']}")->json('data');
echo $latest['status'];
```

---

## Next steps

- **Get notified automatically** — set up [webhooks](/docs/webhooks.md) instead of polling.
- **Understand the envelope and filtering** — see [Querying data](/docs/queries.md).
- **Offer more payment methods** — PIX, Boleto, PayID and more in [Collecting payments](/docs/guides/collecting-payments.md).
- **Bill over time** — [orders](/docs/guides/creating-orders.md) and [subscriptions](/docs/guides/creating-subscriptions.md) for plans and recurring payments.
- **Make requests safely retryable** — [Idempotency](/docs/idempotency.md).
- **Handle failures well** — [Errors](/docs/errors.md).
- **Explore everything** — the full [API reference](https://v1-spec.qualyhq.com/).
```
