---
**📚 Main Documentation:** [Hoko API Documentation (llms.txt)](https://hoko.to/docs/llms.txt)
This is an individual endpoint documentation file. For the complete API reference, see the main documentation above.
---
# Track sale events
**Category:** Conversion Tracking
## Endpoint

POST /api/track/sale

Track a sale conversion event for a short link. This endpoint creates a sale event and upserts a customer record.

**Endpoint**

```text
POST /api/track/sale
```

## Authentication

Requires authentication with an API key that has the conversionsWrite scope.

## Request Body

Accepts a single object (not an array). All fields are required unless marked optional.

| Parameter          | Type           | Required | Location | Description                                                           |
| ------------------ | -------------- | -------- | -------- | --------------------------------------------------------------------- |
| customerExternalId | string         | Yes      | body     | Customer external ID (1-100 characters). Used to upsert the customer. |
| amount             | integer        | Yes      | body     | Sale amount (non-negative integer).                                   |
| currency           | string         | No       | body     | Currency code. Defaults to "usd".                                     |
| eventName          | string         | No       | body     | Sale event name. Defaults to "Purchase" (max 255 characters).         |
| metadata           | object         | No       | body     | Optional metadata object stored with the sale event.                  |
| leadEventName      | string         | No       | body     | Lead event name to attribute this sale to a specific lead.            |
| clickId            | string         | No       | body     | Click ID for direct sale attribution.                                 |
| customerName       | string         | Yes      | body     | Customer name (max 100 characters).                                   |
| customerEmail      | string (email) | No       | body     | Customer email. Optional. Can be null to clear.                       |
| customerAvatar     | string (URL)   | No       | body     | Customer avatar URL. Optional. Can be null to clear.                  |

> **Tip: Capture clickId in the browser**
> If you want to attribute a sale directly to a click, use the Browser attribution script to persist `hoko_id` and pass it as clickId. See [Browser attribution script](/docs/track/analytics-js).

> **Info: Attribution Priority**
> The sale is attributed in this order: leadEventName (if provided) → clickId (if provided) → most recent lead for the customer. If no lead exists and no clickId is provided, the request fails.

## Status Codes

### 201

Sale event tracked successfully.

**Request**

```bash
curl -X POST "https://hoko.to/api/track/sale" \
  -H "Authorization: Bearer <API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "customerExternalId": "customer_123",
    "amount": 12900,
    "currency": "usd",
    "eventName": "Invoice paid",
    "customerName": "John Doe",
    "customerEmail": "john@example.com"
  }'
```

**Request**

```javascript
const response = await fetch('https://hoko.to/api/track/sale', {
	method: 'POST',
	headers: {
		Authorization: 'Bearer <API_KEY>',
		'Content-Type': 'application/json'
	},
	body: JSON.stringify({
		customerExternalId: 'customer_123',
		amount: 12900,
		currency: 'usd',
		eventName: 'Invoice paid',
		customerName: 'John Doe',
		customerEmail: 'john@example.com'
	})
});
const result = await response.json();
```

**Response**

```json
{
	"click": { "id": "click_abc123" },
	"link": {
		"id": "link_xyz",
		"url": "https://example.com",
		"shortUrl": "https://hoko.to/abc123",
		"qrCode": "https://hoko.to/qrcode?text=https%3A%2F%2Fhoko.to%2Fabc123%3Fqr%3D1&size=512&errorCorrection=H&foreground=000000&background=FFFFFF&margin=1&format=svg",
		"title": null,
		"description": null,
		"image": null,
		"utm": null,
		"collectionId": "550e8400-e29b-41d4-a716-446655440000",
		"partnerId": "partner_123",
		"tenantId": "tenant_456",
		"externalId": "ext_789",
		"createdAt": "2024-01-01T00:00:00Z"
	},
	"customer": {
		"id": "customer_123",
		"name": "John Doe",
		"email": "john@example.com",
		"phone": null,
		"avatar": null,
		"externalId": "customer_123"
	},
	"sale": {
		"amount": 12900,
		"currency": "usd",
		"eventName": "Invoice paid",
		"metadata": {
			"invoiceId": "inv_123"
		}
	}
}
```

### 400

Invalid request body, negative amount, or missing attribution data.

**Request**

```bash
curl -X POST "https://hoko.to/api/track/sale" \
  -H "Authorization: Bearer <API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "customerExternalId": "customer_123",
    "amount": -100,
    "customerName": "John Doe"
  }'
```

**Request**

```javascript
const response = await fetch('https://hoko.to/api/track/sale', {
	method: 'POST',
	headers: {
		Authorization: 'Bearer <API_KEY>',
		'Content-Type': 'application/json'
	},
	body: JSON.stringify({
		customerExternalId: 'customer_123',
		amount: -100,
		customerName: 'John Doe'
	})
});
```

**Response**

```json
{
	"error": {
		"en": "Invalid request",
		"ar": "طلب غير صالح"
	}
}
```

### 401

Invalid or missing API key.

**Request**

```bash
curl -X POST "https://hoko.to/api/track/sale" \
  -H "Authorization: Bearer invalid_key"
```

**Request**

```javascript
const response = await fetch('https://hoko.to/api/track/sale', {
	method: 'POST',
	headers: {
		Authorization: 'Bearer invalid_key'
	}
});
```

**Response**

```json
{
	"error": {
		"en": "Invalid API key",
		"ar": "مفتاح API غير صالح"
	}
}
```

### 403

API key does not have the required conversionsWrite scope.

**Request**

```bash
curl -X POST "https://hoko.to/api/track/sale" \
  -H "Authorization: Bearer <API_KEY>"
```

**Request**

```javascript
const response = await fetch('https://hoko.to/api/track/sale', {
	method: 'POST',
	headers: {
		Authorization: 'Bearer <API_KEY>'
	}
});
```

**Response**

```json
{
	"error": {
		"en": "Missing required scopes",
		"ar": "الصلاحيات المطلوبة مفقودة"
	},
	"missingScopes": ["conversionsWrite"]
}
```

### 429

Rate limit exceeded. Check X-RateLimit-\* headers for details.

**Request**

```bash
curl -X POST "https://hoko.to/api/track/sale" \
  -H "Authorization: Bearer <API_KEY>"
```

**Request**

```javascript
const response = await fetch('https://hoko.to/api/track/sale', {
	method: 'POST',
	headers: {
		Authorization: 'Bearer <API_KEY>'
	}
});
```

**Response**

```json
{
	"error": {
		"en": "Rate limit exceeded",
		"ar": "تم تجاوز حد المعدل"
	},
	"retryAfter": 60
}
```

---

**Back to main documentation:** [Hoko API Documentation (llms.txt)](https://hoko.to/docs/llms.txt)