# Bulk create links
Source: https://dub.co/docs/api-reference/endpoint/bulk-create-links
post /links/bulk
Bulk create up to 100 links for the authenticated workspace.
Bulk link creation does not support [custom link
previews](https://dub.co/help/article/custom-link-previews). Also, [webhook
events](/concepts/webhooks/introduction) will not be triggered when using this
endpoint.
# Bulk delete links
Source: https://dub.co/docs/api-reference/endpoint/bulk-delete-links
delete /links/bulk
Bulk delete up to 100 links for the authenticated workspace.
This is a destructive action and cannot be undone. Proceed with caution. Also,
[webhook events](/concepts/webhooks/introduction) will not be triggered when
using this endpoint.
# Bulk update links
Source: https://dub.co/docs/api-reference/endpoint/bulk-update-links
patch /links/bulk
Bulk update up to 100 links with the same data for the authenticated workspace.
This endpoint lets you update up to 100 links **with the same data**.
Some potential use cases:
* Tagging multiple links at once
* Setting the same expiration date for multiple links
* Updating UTM parameters for multiple links
You cannot update the domain or key of a link with this endpoint. Also,
[webhook events](/concepts/webhooks/introduction) will not be triggered when
using this endpoint.
# Check the availability of one or more domains
Source: https://dub.co/docs/api-reference/endpoint/check-a-domain-availability
get /domains/status
Check if a domain name is available for purchase. You can check multiple domains at once.
Checking a domain availability requires an [Enterprise
plan](https://dub.co/enterprise)
# Create a domain
Source: https://dub.co/docs/api-reference/endpoint/create-a-domain
post /domains
Create a domain for the authenticated workspace.
# Create a folder
Source: https://dub.co/docs/api-reference/endpoint/create-a-folder
post /folders
Create a folder for the authenticated workspace.
# Create a link
Source: https://dub.co/docs/api-reference/endpoint/create-a-link
post /links
Create a link for the authenticated workspace.
# Create a partner
Source: https://dub.co/docs/api-reference/endpoint/create-a-partner
post /partners
Create a partner for a program. If partner exists, automatically enrolls them.
Partners endpoints require an [Advanced plan](https://dub.co/pricing)
subscription or higher.
# Create a link for a partner
Source: https://dub.co/docs/api-reference/endpoint/create-a-partner-link
post /partners/links
Create a link for a partner that is enrolled in your program.
Partners endpoints require an [Advanced plan](https://dub.co/pricing)
subscription or higher.
# Create a tag
Source: https://dub.co/docs/api-reference/endpoint/create-a-tag
post /tags
Create a tag for the authenticated workspace.
# Delete a customer
Source: https://dub.co/docs/api-reference/endpoint/delete-a-customer
delete /customers/{id}
Delete a customer from a workspace.
# Delete a domain
Source: https://dub.co/docs/api-reference/endpoint/delete-a-domain
delete /domains/{slug}
Delete a domain from a workspace. It cannot be undone. This will also delete all the links associated with the domain.
# Delete a folder
Source: https://dub.co/docs/api-reference/endpoint/delete-a-folder
delete /folders/{id}
Delete a folder from the workspace. All existing links will still work, but they will no longer be associated with this folder.
# Delete a link
Source: https://dub.co/docs/api-reference/endpoint/delete-a-link
delete /links/{linkId}
Delete a link for the authenticated workspace.
# Register a domain
Source: https://dub.co/docs/api-reference/endpoint/register-a-domain
post /domains/register
Register a domain for the authenticated workspace. Only available for Enterprise Plans.
Domain registration is only available for certain [Enterprise
customers](https://dub.co/enterprise). Please [contact
us](https://dub.co/contact/sales) to get access.
# Retrieve a customer
Source: https://dub.co/docs/api-reference/endpoint/retrieve-a-customer
get /customers/{id}
Retrieve a customer by ID for the authenticated workspace.
# Retrieve a link
Source: https://dub.co/docs/api-reference/endpoint/retrieve-a-link
get /links/info
Retrieve the info for a link.
You can retrieve a link by providing one of the following as a query parameter:
* `domain` and `key`.
* `linkId`.
* `externalId`.
# List all commissions
Source: https://dub.co/docs/api-reference/endpoint/retrieve-a-list-of-commissions
get /commissions
Retrieve a list of commissions for a program.
Commissions endpoints require an [Business plan](https://dub.co/pricing)
subscription or higher.
# List all customers
Source: https://dub.co/docs/api-reference/endpoint/retrieve-a-list-of-customers
get /customers
Retrieve a list of customers for the authenticated workspace.
# Retrieve a list of events
Source: https://dub.co/docs/api-reference/endpoint/retrieve-a-list-of-events
get /events
Retrieve a paginated list of events for the authenticated workspace.
Events endpoints require a [Business plan](https://dub.co/pricing)
subscription or higher.
# Retrieve a list of folders
Source: https://dub.co/docs/api-reference/endpoint/retrieve-a-list-of-folders
get /folders
Retrieve a list of folders for the authenticated workspace.
# Retrieve a list of links
Source: https://dub.co/docs/api-reference/endpoint/retrieve-a-list-of-links
get /links
Retrieve a paginated list of links for the authenticated workspace.
# Retrieve a list of tags
Source: https://dub.co/docs/api-reference/endpoint/retrieve-a-list-of-tags
get /tags
Retrieve a list of tags for the authenticated workspace.
# Retrieve a partner's links.
Source: https://dub.co/docs/api-reference/endpoint/retrieve-a-partners-links
get /partners/links
Retrieve a partner's links by their partner ID or tenant ID.
Partners endpoints require an [Advanced plan](https://dub.co/pricing)
subscription or higher.
# Retrieve analytics
Source: https://dub.co/docs/api-reference/endpoint/retrieve-analytics
get /analytics
Retrieve analytics for a link, a domain, or the authenticated workspace. The response type depends on the `event` and `type` query parameters.
Analytics endpoints require a [Pro plan](https://dub.co/pricing) subscription
or higher.
# Retrieve links count
Source: https://dub.co/docs/api-reference/endpoint/retrieve-number-of-links
get /links/count
Retrieve the number of links for the authenticated workspace. The provided query parameters allow filtering the returned links.
# Retrieve analytics for a partner
Source: https://dub.co/docs/api-reference/endpoint/retrieve-partner-analytics
get /partners/analytics
Retrieve analytics for a partner within a program. The response type vary based on the `groupBy` query parameter.
Partners endpoints require an [Advanced plan](https://dub.co/pricing)
subscription or higher.
# Track a lead
Source: https://dub.co/docs/api-reference/endpoint/track-lead
post /track/lead
Track a lead for a short link.
Conversions endpoints require a [Business plan](https://dub.co/pricing)
subscription or higher.
# Track a sale
Source: https://dub.co/docs/api-reference/endpoint/track-sale
post /track/sale
Track a sale for a short link.
Conversions endpoints require a [Business plan](https://dub.co/pricing)
subscription or higher.
# Update a commission.
Source: https://dub.co/docs/api-reference/endpoint/update-a-commission
patch /commissions/{id}
Update an existing commission amount. This is useful for handling refunds (partial or full) or fraudulent sales.
Commissions endpoints require an [Business plan](https://dub.co/pricing)
subscription or higher.
# Update a customer
Source: https://dub.co/docs/api-reference/endpoint/update-a-customer
patch /customers/{id}
Update a customer for the authenticated workspace.
# Update a domain
Source: https://dub.co/docs/api-reference/endpoint/update-a-domain
patch /domains/{slug}
Update a domain for the authenticated workspace.
# Update a folder
Source: https://dub.co/docs/api-reference/endpoint/update-a-folder
patch /folders/{id}
Update a folder in the workspace.
# Update a link
Source: https://dub.co/docs/api-reference/endpoint/update-a-link
patch /links/{linkId}
Update a link for the authenticated workspace. If there's no change, returns it as it is.
# Update a tag
Source: https://dub.co/docs/api-reference/endpoint/update-a-tag
patch /tags/{id}
Update a tag in the workspace.
# Upsert a link
Source: https://dub.co/docs/api-reference/endpoint/upsert-a-link
put /links/upsert
Upsert a link for the authenticated workspace by its URL. If a link with the same URL already exists, return it (or update it if there are any changes). Otherwise, a new link will be created.
# Upsert a link for a partner
Source: https://dub.co/docs/api-reference/endpoint/upsert-a-partner-link
put /partners/links/upsert
Upsert a link for a partner that is enrolled in your program. If a link with the same URL already exists, return it (or update it if there are any changes). Otherwise, a new link will be created.
Partners endpoints require an [Advanced plan](https://dub.co/pricing)
subscription or higher.
# Introduction
Source: https://dub.co/docs/api-reference/introduction
Fundamental concepts of Dub's API.
## Base URL
Dub's API is built on REST principles and is served over HTTPS. To ensure data privacy, unencrypted HTTP is not supported.
The Base URL for all API endpoints is:
```bash Terminal
https://api.dub.co
```
## Authentication
Authentication to Dub's API is performed via the Authorization header with a Bearer token. To authenticate, you need to include the Authorization header with the word `Bearer` followed by your API key in your requests like so:
```bash Terminal
Authorization: Bearer dub_xxxxxx
```
Here are examples of how to authenticate with Dub's API in different programming languages:
```bash cURL
curl --request GET \
--url https://api.dub.co/links \
--header 'Authorization: Bearer dub_xxxxxx'
```
```javascript Node.js
import { Dub } from "dub";
const dub = new Dub({
token: "dub_xxxxxx",
});
// Make API calls
const links = await dub.links.list();
```
```python Python
from dub import Dub
client = Dub(api_key="dub_xxxxxx")
# Make API calls
links = client.links.list()
```
```go Go
import (
"context"
"github.com/dubinc/dub-go"
)
client := dub.NewClient("dub_xxxxxx")
// Make API calls
ctx := context.Background()
links, err := client.Links.List(ctx)
```
```ruby Ruby
require 'dub'
client = Dub::Client.new(api_key: "dub_xxxxxx")
# Make API calls
links = client.links.list
```
```php PHP
use Dub\Client;
$client = new Client([
'api_key' => 'dub_xxxxxx'
]);
// Make API calls
$links = $client->links->list();
```
Learn more about [how to get your API key](/api-reference/tokens).
## Native SDKs
Dub offers native SDKs in some of the most popular programming languages:
* [TypeScript SDK](/sdks/typescript)
* [Python SDK](/sdks/python)
* [Ruby SDK](/sdks/ruby)
* [PHP SDK](/sdks/php)
* [Go SDK](/sdks/go)
You can find the full list of SDKs [here](/sdks/overview).
## Error Handling
Dub API returns machine readable error codes, human readable error messages and a link to the docs for more information.
Here is how an error response looks like:
```json
{
"error": {
"code": "not_found",
"message": "The requested resource was not found.",
"doc_url": "https://dub.co/docs/api-reference/errors#not-found"
}
}
```
Here is a list of all error codes Dub API returns:
* **Status:** 400
* **Problem:** The request is malformed, either missing required fields, using wrong datatypes, or being syntactically incorrect.
* **Solution:** Check the request and make sure it is properly formatted.
* **Status:** 401
* **Problem:** The request has not been applied because it lacks valid authentication credentials for the target resource.
* **Solution:** Make sure you are using the correct API key or access token.
* **Status:** 403
* **Problem:** The server understood the request, but is refusing to fulfill it because the client lacks proper permission.
* **Solution:** Make sure you have the necessary permissions to access the resource.
* **Status:** 404
* **Problem:** The server has not found anything matching the request URI.
* **Solution:** Check the request and make sure the resource exists.
* **Status:** 409
* **Problem:** Another resource already uses the same identifier. For example, workspace slug must be unique.
* **Solution:** Change the identifier to a unique value.
* **Status:** 410
* **Problem:** The invite has expired.
* **Solution:** Generate a new invite.
* **Status:** 422
* **Problem:** The server was unable to process the request because it contains invalid data.
* **Solution:** Check the request and make sure input data is valid.
* **Status:** 429
* **Problem:** The request has been rate limited.
* **Solution:** Wait for a while and try again.
* **Status:** 500
* **Problem:** The server encountered an unexpected condition that prevented it from fulfilling the request.
* **Solution:** Try again later. If the problem persists, contact support.
## Pagination
Dub's API supports pagination. This is useful when you have a large number of resources and you want to retrieve them in smaller chunks.
These list API methods share a common set of parameters that allow you to control the number of items returned and the page number. For example, you can:
* [retrieve a list of links](/api-reference/endpoint/retrieve-a-list-of-links)
* [retrieve a list of domains](/api-reference/endpoint/retrieve-a-list-of-domains)
* [retrieve a list of events](/api-reference/endpoint/retrieve-a-list-of-events)
### Parameters
The page number to retrieve. By default, the first page is returned.
The number of items to retrieve per page. The default value varies by
endpoint. Maximum value is 100.
The field to sort the results by.
The order to sort the results by. Can be `asc` or `desc`.
### Example
The following example demonstrates how to retrieve the first page of 10 links:
```bash cURL
curl --request GET \
--url https://api.dub.co/links?page=1&pageSize=10 \
--header 'Authorization: Bearer '
```
```javascript Node.js
const res = await dub.links.list({
page: 1,
pageSize: 10,
});
```
```python Python
res = s.links.list(request={
"page": 1,
"page_size": 10,
})
```
```go Go
request := operations.GetLinksRequest{
Page: dubgo.Float64(1),
PageSize: dubgo.Float64(10),
}
ctx := context.Background()
res, err := s.Links.List(ctx, request)
```
```ruby Ruby
req = ::OpenApiSDK::Operations::GetLinksRequest.new(
page: 1,
page_size: 10,
)
res = s.links.list(req)
```
# Rate limits
Source: https://dub.co/docs/api-reference/rate-limits
Learn about Dub's API rate limits.
Dub's API rate limiting is in conformance with the [IETF standard](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-ratelimit-headers):
| Header Name | Description |
| ----------------------- | ------------------------------------------------------------------------------- |
| `X-RateLimit-Limit` | The maximum number of requests that the consumer is permitted to make per hour. |
| `X-RateLimit-Remaining` | The number of requests remaining in the current rate limit window. |
| `X-RateLimit-Reset` | The time at which the current rate limit window resets in UTC epoch seconds. |
| `Retry-After` | The number of seconds to wait before retrying the request again. |
Dub's API is capped at **60 requests per minute** per key on the Free plan, with elevated limits for [Pro plan](https://dub.co/help/article/pro-plan) and above.
This is implemented to ensure a fair usage policy so that excessive use by a single user does not adversely affect the performance and usage of the API by others.
You'll receive a `429 Too Many Requests` response code if the rate limit is exceeded.
## Rate limits by plan
Depending on your Dub plan, you can expect the following rate limits:
| Plan | Rate limit |
| --------------------------------------- | ----------------------------------------------------------------------- |
| Free | 60 requests per minute |
| [Pro](https://dub.co/pricing) | 600 requests per minute |
| [Business](https://dub.co/pricing) | 1,200 requests per minute |
| [Advanced](https://dub.co/pricing) | 3,000 requests per minute |
| [Enterprise](https://dub.co/enterprise) | Custom – [reach out to sales](https://dub.co/contact/sales) for details |
## How to comply with rate limits
Here are some tips on how you can optimize your API setup to comply with our rate limits:
### 1. Bulk link creation
If you need to create a lot of links within a short period of time, try our [bulk link creation endpoint](/api-reference/endpoint/bulk-create-links) instead (create up to 100 links in one API call)
```javascript Node.js
await dub.links.createMany([
{
url: "https://google.com",
},
{
url: "https://twitter.com",
},
{
url: "https://linkedin.com",
},
]);
```
```python Python
res = d.links.create_many(request=[
{
url: "https://google.com",
},
{
url: "https://twitter.com",
},
{
url: "https://linkedin.com",
},
]);
```
```go Go
var request []operations.RequestBody =
[]operations.RequestBody{
operations.RequestBody{
URL: "https://google.com",
},
operations.RequestBody{
URL: "https://twitter.com",
},
operations.RequestBody{
URL: "https://linkedin.com",
},
}
ctx := context.Background()
res, err := s.Links.CreateMany(ctx, request)
```
```ruby Ruby
s.links.create_many(
::OpenApiSDK::Operations::BulkCreateLinksRequest.new(
request_body: [
::OpenApiSDK::Operations::RequestBody.new(
url: "https://google.com",
),
::OpenApiSDK::Operations::RequestBody.new(
url: "https://twitter.com",
),
::OpenApiSDK::Operations::RequestBody.new(
url: "https://linkedin.com",
),
]
)
)
```
### 2. Fetch workspace-level analytics
If you're using our [Analytics API](/api-reference/endpoint/retrieve-analytics), instead of retrieving the clicks count for every single link, try making a single API call to get workspace-level click analytics instead.
```javascript Node.js
await dub.analytics.retrieve({
groupBy: "top_links",
start: "4 hours ago", // we support natural language for start/end params
});
```
```python Python
res = d.analytics.retrieve(request={
"groupBy": "top_links",
"start": "4 hours ago", // we support natural language for start/end params
})
```
```go Go
func main() {
// Retrieve the timeseries analytics for the last 7 days for a link
request := operations.RetrieveAnalyticsRequest{
GroupBy: "top_links",
Start: "4 hours ago", // we support natural language for start/end params
}
ctx := context.Background()
res, err := d.Analytics.Retrieve(ctx, request)
if err != nil {
log.Fatal(err)
}
if res.OneOf != nil {
// handle response
}
}
```
```ruby Ruby
req = ::OpenApiSDK::Operations::RetrieveAnalyticsRequest.new(
group_by: ::OpenApiSDK::Operations::GroupBy::TOP_LINKS,
start: "4 hours ago", // we support natural language for start/end params
)
res = dub.analytics.retrieve(req)
puts res.raw_response.body
```
### 3. Leverage webhooks
If you're expecting high volume, fast-changing data (e.g. thousands of clicks on your links per day) and would prefer to store the data on your end, we recommend using our [real-time webhooks feature](https://dub.co/blog/introducing-webhooks) instead.
[Webhooks](/concepts/webhooks/introduction) are *push-based*, meaning that Dub will send events to your webhook receiver endpoint when specific events occur, while a REST API is *pull-based*, meaning that you need to consistently poll the API endpoint to get real-time updates.
Check out our [webhooks documentation](/concepts/webhooks/introduction) to learn more.
# API keys
Source: https://dub.co/docs/api-reference/tokens
Learn how API keys work on Dub.
API keys on Dub allow you to access your workspace programmatically. This is useful for integrating Dub into your application or with other tools and services.
Each API key is tied to a specific workspace – meaning you can use it to access that workspace's resources without having to worry about "leaking" access to other workspaces.
API keys on Dub follow the format:
```bash .env
DUB_API_KEY=dub_xxxxxxxx
```
By default, you can use this key to perform any API request without restriction, so it must be stored securely in your app's server-side code (such as in an environment variable or credential management system). Don’t expose this key on a website.
## Create an API key
You can create an API key by following these steps:
Go to **Settings** > [**API Keys**](https://app.dub.co/settings/tokens) in your workspace.
Click on the "Create" button and select permissions you want to grant to
the API key.
Select between "You" and "Machine" to associate the API key with your account or a [machine user](#machine-users).
* **You**: This API key is tied to your user and can make requests against the selected workspace.
* **Machine**: A machine user will be added to your workspace, and an API key associated with that machine user will be created.
Click on the **Create API Key** button to create the key. Make sure to copy your API key and store it in a safe place. You won't be able to see it again.
Now that you have your API key, you can use it to access your workspace's resources programmatically via SDKs or within any API request as a bearer token.
```
Authorization: Bearer dub_xxxx
```
We recommend creating API keys with the least privilege necessary to perform
the required tasks. This helps to reduce the risk of unauthorized access to
your workspace.
## API key permissions
When creating a secret key, you can select the permissions it has, which will give the key access to certain (or all) resources on Dub. Here are the different permission options:
| Permission | Description |
| :------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **All permissions** | This API key will have full access to all resources. |
| **Read only** | This API key will have read-only access to all resources. |
| **Restricted** | This API key will have restricted access to some resources:
|
Depending on your use case, you might want to use one of these 3 options to limit the scope of the API key and improve security. When making API calls, if your API key has insufficient permissions, the error should tell you which permissions you need.
You can only set permissions on Secret keys. Publishable keys only have access
to certain endpoints, and cannot be restricted.
## Machine users
On Dub, you can create API keys that are associated with a "Machine user". This is particularly helpful when you don't want to associate the API key with a particular user in your workspace, to avoid security risks in involving turnover or changes in project ownership.
Machine users share the same permissions as the [owner
role](https://dub.co/help/article/workspace-roles#owner-role) in a workspace.
Make sure to only create machine users for trusted applications.
These machine users will show up on your workspace's **People** tab, but will not contribute to your workspace's user count.
If you delete an API key associated with a machine user, the machine user will
be deleted. Vice versa, if you delete a machine user, their corresponding API
key will be deleted as well.
# Device data
Source: https://dub.co/docs/concepts/analytics/device
Analytics endpoints require a [Pro plan](https://dub.co/pricing) subscription
or higher.
Device data allows you to analyze how users interact with your links across different devices, browsers, and operating systems.
## Device analytics
The top devices by event count, including device names.
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const result = await dub.analytics.retrieve({
event: "clicks",
groupBy: "devices",
linkId: "clux0rgak00011...",
interval: "30d",
});
```
```python Python
from dub import Dub
with Dub(
token="DUB_API_KEY",
) as d_client:
res = d_client.analytics.retrieve(request={
"event": "clicks",
"groupBy": "devices",
"linkId": "clux0rgak00011...",
"interval": "30d",
})
print(res)
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()
->setSecurity('DUB_API_KEY')
->build();
$request = new Operations\RetrieveAnalyticsRequest(
event: 'clicks',
groupBy: 'devices',
linkId: 'clux0rgak00011...',
interval: '30d',
);
$response = $sdk->analytics->retrieve(
request: $request
);
if ($response->oneOf !== null) {
// handle response
}
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity("DUB_API_KEY"),
)
res, err := s.Analytics.Retrieve(ctx, operations.RetrieveAnalyticsRequest{
Event: dubgo.String("clicks"),
GroupBy: dubgo.String("devices"),
LinkID: dubgo.String("clux0rgak00011..."),
Interval: dubgo.String("30d"),
})
if err != nil {
log.Fatal(err)
}
if res != nil {
// handle response
}
}
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new(
security: ::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
),
)
req = ::OpenApiSDK::Operations::RetrieveAnalyticsRequest.new(
event: "clicks",
groupBy: "devices",
linkId: "clux0rgak00011...",
interval: "30d",
)
res = s.analytics.retrieve(req)
if ! res.one_of.nil?
# handle response
end
```
## Browser analytics
The top browsers by event count, including browser names.
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const result = await dub.analytics.retrieve({
event: "clicks",
groupBy: "browsers",
linkId: "clux0rgak00011...",
interval: "30d",
});
```
```python Python
from dub import Dub
with Dub(
token="DUB_API_KEY",
) as d_client:
res = d_client.analytics.retrieve(request={
"event": "clicks",
"groupBy": "browsers",
"linkId": "clux0rgak00011...",
"interval": "30d",
})
print(res)
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()
->setSecurity('DUB_API_KEY')
->build();
$request = new Operations\RetrieveAnalyticsRequest(
event: 'clicks',
groupBy: 'browsers',
linkId: 'clux0rgak00011...',
interval: '30d',
);
$response = $sdk->analytics->retrieve(
request: $request
);
if ($response->oneOf !== null) {
// handle response
}
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity("DUB_API_KEY"),
)
res, err := s.Analytics.Retrieve(ctx, operations.RetrieveAnalyticsRequest{
Event: dubgo.String("clicks"),
GroupBy: dubgo.String("browsers"),
LinkID: dubgo.String("clux0rgak00011..."),
Interval: dubgo.String("30d"),
})
if err != nil {
log.Fatal(err)
}
if res != nil {
// handle response
}
}
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new(
security: ::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
),
)
req = ::OpenApiSDK::Operations::RetrieveAnalyticsRequest.new(
event: "clicks",
groupBy: "browsers",
linkId: "clux0rgak00011...",
interval: "30d",
)
res = s.analytics.retrieve(req)
if ! res.one_of.nil?
# handle response
end
```
## Operating system analytics
The top operating systems by event count, including OS names.
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const result = await dub.analytics.retrieve({
event: "clicks",
groupBy: "os",
linkId: "clux0rgak00011...",
interval: "30d",
});
```
```python Python
from dub import Dub
with Dub(
token="DUB_API_KEY",
) as d_client:
res = d_client.analytics.retrieve(request={
"event": "clicks",
"groupBy": "os",
"linkId": "clux0rgak00011...",
"interval": "30d",
})
print(res)
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()
->setSecurity('DUB_API_KEY')
->build();
$request = new Operations\RetrieveAnalyticsRequest(
event: 'clicks',
groupBy: 'os',
linkId: 'clux0rgak00011...',
interval: '30d',
);
$response = $sdk->analytics->retrieve(
request: $request
);
if ($response->oneOf !== null) {
// handle response
}
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity("DUB_API_KEY"),
)
res, err := s.Analytics.Retrieve(ctx, operations.RetrieveAnalyticsRequest{
Event: dubgo.String("clicks"),
GroupBy: dubgo.String("os"),
LinkID: dubgo.String("clux0rgak00011..."),
Interval: dubgo.String("30d"),
})
if err != nil {
log.Fatal(err)
}
if res != nil {
// handle response
}
}
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new(
security: ::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
),
)
req = ::OpenApiSDK::Operations::RetrieveAnalyticsRequest.new(
event: "clicks",
groupBy: "os",
linkId: "clux0rgak00011...",
interval: "30d",
)
res = s.analytics.retrieve(req)
if ! res.one_of.nil?
# handle response
end
```
# Introduction
Source: https://dub.co/docs/concepts/analytics/introduction
Learn how to use Dub's Analytics API to build real-time analytics dashboards for your links.
Analytics endpoints require a [Pro plan](https://dub.co/pricing) subscription
or higher.
Dub's [Analytics API](/api-reference/endpoint/retrieve-analytics) allows you to build real-time analytics dashboards for your links that lives directly inside your application.
## Fundamentals
At a high level, Dub's Analytics API allows you to retrieve data about your links by event type and group by different dimensions.
Dub's Analytics API supports the following `event` types:
* `clicks` – the number of clicks on a link
* `leads` – the number of leads generated from a link
* `sales` – the number of sales generated from a link
Each of these events can be aggregated in different ways by using the `groupBy` parameter:
The total number of events over the specified time period.
Example response:
```json
{
"clicks": 100,
"leads": 5,
"sales": 2,
"saleAmount": 5000
}
```
The number of events over a given time interval, broken down by time periods.
Example response:
```json
[
{
"start": "2024-01-01T00:00:00.000Z",
"clicks": 10,
"leads": 1,
"sales": 0,
"saleAmount": 0
},
{
"start": "2024-01-02T00:00:00.000Z",
"clicks": 15,
"leads": 2,
"sales": 1,
"saleAmount": 2500
}
]
```
The top links by event count, including link metadata.
Example response:
```json
[
{
"id": "clux0rgak00011...",
"domain": "dub.co",
"key": "github",
"shortLink": "https://dub.co/github",
"url": "https://github.com",
"title": "GitHub",
"createdAt": "2024-01-01T00:00:00.000Z",
"clicks": 50,
"leads": 3,
"sales": 1,
"saleAmount": 2500
}
]
```
The top countries by event count, using ISO 3166-1 alpha-2 country codes.
Example response:
```json
[
{
"country": "US",
"region": "*",
"city": "*",
"clicks": 30,
"leads": 2,
"sales": 1,
"saleAmount": 2500
}
]
```
The top cities by event count, including city names and ISO 3166-1 alpha-2 country codes.
Example response:
```json
[
{
"country": "US",
"region": "CA",
"city": "San Francisco",
"clicks": 20,
"leads": 1,
"sales": 1,
"saleAmount": 2500
}
]
```
The top regions by event count, including region codes and ISO 3166-1 alpha-2 country codes.
Example response:
```json
[
{
"country": "US",
"region": "CA",
"city": "*",
"clicks": 25,
"leads": 2,
"sales": 1,
"saleAmount": 2500
}
]
```
The top continents by event count, using 2-letter ISO continent codes.
Example response:
```json
[
{
"continent": "NA",
"clicks": 40,
"leads": 3,
"sales": 2,
"saleAmount": 5000
}
]
```
The top devices by event count, including device names.
Example response:
```json
[
{
"device": "iPhone",
"clicks": 35,
"leads": 2,
"sales": 1,
"saleAmount": 2500
}
]
```
The top browsers by event count, including browser names.
Example response:
```json
[
{
"browser": "Chrome",
"clicks": 45,
"leads": 3,
"sales": 2,
"saleAmount": 5000
}
]
```
The top operating systems by event count, including OS names.
Example response:
```json
[
{
"os": "iOS",
"clicks": 30,
"leads": 2,
"sales": 1,
"saleAmount": 2500
}
]
```
The top referrers by event count, including referrer names.
Example response:
```json
[
{
"referer": "Google",
"clicks": 25,
"leads": 2,
"sales": 1,
"saleAmount": 2500
}
]
```
The top referrer URLs by event count, including full URLs.
Example response:
```json
[
{
"refererUrl": "https://www.google.com",
"clicks": 20,
"leads": 1,
"sales": 1,
"saleAmount": 2500
}
]
```
The top UTM sources by event count.
Example response:
```json
[
{
"utm_source": "newsletter",
"clicks": 25,
"leads": 2,
"sales": 1,
"saleAmount": 2500
}
]
```
The top UTM mediums by event count.
Example response:
```json
[
{
"utm_medium": "email",
"clicks": 20,
"leads": 1,
"sales": 1,
"saleAmount": 2500
}
]
```
The top UTM campaigns by event count.
Example response:
```json
[
{
"utm_campaign": "summer_sale",
"clicks": 30,
"leads": 2,
"sales": 1,
"saleAmount": 2500
}
]
```
The top UTM terms by event count.
Example response:
```json
[
{
"utm_term": "discount",
"clicks": 15,
"leads": 1,
"sales": 1,
"saleAmount": 2500
}
]
```
The top UTM contents by event count.
Example response:
```json
[
{
"utm_content": "banner",
"clicks": 20,
"leads": 1,
"sales": 1,
"saleAmount": 2500
}
]
```
## Example queries
Here are some examples of how to retrieve data using Dub's [Analytics API](/api-reference/endpoint/retrieve-analytics):
* [Total event count](#total-event-count)
* [Timeseries data](#timeseries-data)
* [Top links by event](#top-links-by-event)
### Total event count
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const result = await dub.analytics.retrieve({
event: "clicks",
groupBy: "count",
linkId: "clux0rgak00011...",
interval: "30d",
});
```
```python Python
from dub import Dub
with Dub(
token="DUB_API_KEY",
) as d_client:
res = d_client.analytics.retrieve(request={
"event": "clicks",
"groupBy": "count",
"linkId": "clux0rgak00011...",
"interval": "30d",
})
print(res)
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()
->setSecurity('DUB_API_KEY')
->build();
$request = new Operations\RetrieveAnalyticsRequest(
event: 'clicks',
groupBy: 'count',
linkId: 'clux0rgak00011...',
interval: '30d',
);
$response = $sdk->analytics->retrieve(
request: $request
);
if ($response->oneOf !== null) {
// handle response
}
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity("DUB_API_KEY"),
)
res, err := s.Analytics.Retrieve(ctx, operations.RetrieveAnalyticsRequest{
Event: dubgo.String("clicks"),
GroupBy: dubgo.String("count"),
LinkID: dubgo.String("clux0rgak00011..."),
Interval: dubgo.String("30d"),
})
if err != nil {
log.Fatal(err)
}
if res != nil {
// handle response
}
}
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new(
security: ::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
),
)
req = ::OpenApiSDK::Operations::RetrieveAnalyticsRequest.new(
event: "clicks",
groupBy: "count",
linkId: "clux0rgak00011...",
interval: "30d",
)
res = s.analytics.retrieve(req)
if ! res.one_of.nil?
# handle response
end
```
### Timeseries data
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const result = await dub.analytics.retrieve({
event: "clicks",
groupBy: "timeseries",
linkId: "clux0rgak00011...",
interval: "30d",
});
```
```python Python
from dub import Dub
with Dub(
token="DUB_API_KEY",
) as d_client:
res = d_client.analytics.retrieve(request={
"event": "clicks",
"groupBy": "timeseries",
"linkId": "clux0rgak00011...",
"interval": "30d",
})
print(res)
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()
->setSecurity('DUB_API_KEY')
->build();
$request = new Operations\RetrieveAnalyticsRequest(
event: 'clicks',
groupBy: 'timeseries',
linkId: 'clux0rgak00011...',
interval: '30d',
);
$response = $sdk->analytics->retrieve(
request: $request
);
if ($response->oneOf !== null) {
// handle response
}
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity("DUB_API_KEY"),
)
res, err := s.Analytics.Retrieve(ctx, operations.RetrieveAnalyticsRequest{
Event: dubgo.String("clicks"),
GroupBy: dubgo.String("timeseries"),
LinkID: dubgo.String("clux0rgak00011..."),
Interval: dubgo.String("30d"),
})
if err != nil {
log.Fatal(err)
}
if res != nil {
// handle response
}
}
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new(
security: ::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
),
)
req = ::OpenApiSDK::Operations::RetrieveAnalyticsRequest.new(
event: "clicks",
groupBy: "timeseries",
linkId: "clux0rgak00011...",
interval: "30d",
)
res = s.analytics.retrieve(req)
if ! res.one_of.nil?
# handle response
end
```
### Top links by event
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const result = await dub.analytics.retrieve({
event: "clicks",
groupBy: "top_links",
linkId: "clux0rgak00011...",
interval: "30d",
});
```
```python Python
from dub import Dub
with Dub(
token="DUB_API_KEY",
) as d_client:
res = d_client.analytics.retrieve(request={
"event": "clicks",
"groupBy": "top_links",
"linkId": "clux0rgak00011...",
"interval": "30d",
})
print(res)
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()
->setSecurity('DUB_API_KEY')
->build();
$request = new Operations\RetrieveAnalyticsRequest(
event: 'clicks',
groupBy: 'top_links',
linkId: 'clux0rgak00011...',
interval: '30d',
);
$response = $sdk->analytics->retrieve(
request: $request
);
if ($response->oneOf !== null) {
// handle response
}
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity("DUB_API_KEY"),
)
res, err := s.Analytics.Retrieve(ctx, operations.RetrieveAnalyticsRequest{
Event: dubgo.String("clicks"),
GroupBy: dubgo.String("top_links"),
LinkID: dubgo.String("clux0rgak00011..."),
Interval: dubgo.String("30d"),
})
if err != nil {
log.Fatal(err)
}
if res != nil {
// handle response
}
}
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new(
security: ::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
),
)
req = ::OpenApiSDK::Operations::RetrieveAnalyticsRequest.new(
event: "clicks",
groupBy: "top_links",
linkId: "clux0rgak00011...",
interval: "30d",
)
res = s.analytics.retrieve(req)
if ! res.one_of.nil?
# handle response
end
```
## Example apps
With Dub's [Analytics API](/api-reference/endpoint/retrieve-analytics), you can build user-facing analytics dashboards with the real-time click and conversion data for your links.
Here are some open-source examples of how you can use the Analytics API to build your own custom analytics dashboards:
Programmatically shorten links and fetch real-time click analytics with Dub
How Cap.so fetches real-time click analytics for their recording links
And here's another [real-world example](https://x.com/meetassembly/status/1901691081579794505) of a custom analytics dashboard built with the Analytics API:
# Location data
Source: https://dub.co/docs/concepts/analytics/location
Analytics endpoints require a [Pro plan](https://dub.co/pricing) subscription
or higher.
Location data allows you to analyze how users interact with your links across different geographical locations.
## Country analytics
The top countries by event count, including ISO 3166-1 alpha-2 country codes.
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const result = await dub.analytics.retrieve({
event: "clicks",
groupBy: "countries",
linkId: "clux0rgak00011...",
interval: "30d",
});
```
```python Python
from dub import Dub
with Dub(
token="DUB_API_KEY",
) as d_client:
res = d_client.analytics.retrieve(request={
"event": "clicks",
"groupBy": "countries",
"linkId": "clux0rgak00011...",
"interval": "30d",
})
print(res)
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()
->setSecurity('DUB_API_KEY')
->build();
$request = new Operations\RetrieveAnalyticsRequest(
event: 'clicks',
groupBy: 'countries',
linkId: 'clux0rgak00011...',
interval: '30d',
);
$response = $sdk->analytics->retrieve(
request: $request
);
if ($response->oneOf !== null) {
// handle response
}
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity("DUB_API_KEY"),
)
res, err := s.Analytics.Retrieve(ctx, operations.RetrieveAnalyticsRequest{
Event: dubgo.String("clicks"),
GroupBy: dubgo.String("countries"),
LinkID: dubgo.String("clux0rgak00011..."),
Interval: dubgo.String("30d"),
})
if err != nil {
log.Fatal(err)
}
if res != nil {
// handle response
}
}
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new(
security: ::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
),
)
req = ::OpenApiSDK::Operations::RetrieveAnalyticsRequest.new(
event: "clicks",
groupBy: "countries",
linkId: "clux0rgak00011...",
interval: "30d",
)
res = s.analytics.retrieve(req)
if ! res.one_of.nil?
# handle response
end
```
## City analytics
The top cities by event count, including city names and their corresponding region codes + ISO 3166-1 alpha-2 country codes.
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const result = await dub.analytics.retrieve({
event: "clicks",
groupBy: "cities",
linkId: "clux0rgak00011...",
interval: "30d",
});
```
```python Python
from dub import Dub
with Dub(
token="DUB_API_KEY",
) as d_client:
res = d_client.analytics.retrieve(request={
"event": "clicks",
"groupBy": "cities",
"linkId": "clux0rgak00011...",
"interval": "30d",
})
print(res)
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()
->setSecurity('DUB_API_KEY')
->build();
$request = new Operations\RetrieveAnalyticsRequest(
event: 'clicks',
groupBy: 'cities',
linkId: 'clux0rgak00011...',
interval: '30d',
);
$response = $sdk->analytics->retrieve(
request: $request
);
if ($response->oneOf !== null) {
// handle response
}
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity("DUB_API_KEY"),
)
res, err := s.Analytics.Retrieve(ctx, operations.RetrieveAnalyticsRequest{
Event: dubgo.String("clicks"),
GroupBy: dubgo.String("cities"),
LinkID: dubgo.String("clux0rgak00011..."),
Interval: dubgo.String("30d"),
})
if err != nil {
log.Fatal(err)
}
if res != nil {
// handle response
}
}
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new(
security: ::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
),
)
req = ::OpenApiSDK::Operations::RetrieveAnalyticsRequest.new(
event: "clicks",
groupBy: "cities",
linkId: "clux0rgak00011...",
interval: "30d",
)
res = s.analytics.retrieve(req)
if ! res.one_of.nil?
# handle response
end
```
## Continent analytics
The top continents by event count, using 2-letter ISO continent codes.
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const result = await dub.analytics.retrieve({
event: "clicks",
groupBy: "continents",
linkId: "clux0rgak00011...",
interval: "30d",
});
```
```python Python
from dub import Dub
with Dub(
token="DUB_API_KEY",
) as d_client:
res = d_client.analytics.retrieve(request={
"event": "clicks",
"groupBy": "continents",
"linkId": "clux0rgak00011...",
"interval": "30d",
})
print(res)
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()
->setSecurity('DUB_API_KEY')
->build();
$request = new Operations\RetrieveAnalyticsRequest(
event: 'clicks',
groupBy: 'continents',
linkId: 'clux0rgak00011...',
interval: '30d',
);
$response = $sdk->analytics->retrieve(
request: $request
);
if ($response->oneOf !== null) {
// handle response
}
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity("DUB_API_KEY"),
)
res, err := s.Analytics.Retrieve(ctx, operations.RetrieveAnalyticsRequest{
Event: dubgo.String("clicks"),
GroupBy: dubgo.String("continents"),
LinkID: dubgo.String("clux0rgak00011..."),
Interval: dubgo.String("30d"),
})
if err != nil {
log.Fatal(err)
}
if res != nil {
// handle response
}
}
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new(
security: ::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
),
)
req = ::OpenApiSDK::Operations::RetrieveAnalyticsRequest.new(
event: "clicks",
groupBy: "continents",
linkId: "clux0rgak00011...",
interval: "30d",
)
res = s.analytics.retrieve(req)
if ! res.one_of.nil?
# handle response
end
```
## Region analytics
The top regions by event count, including region codes and ISO 3166-1 alpha-2 country codes.
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const result = await dub.analytics.retrieve({
event: "clicks",
groupBy: "regions",
linkId: "clux0rgak00011...",
interval: "30d",
});
```
```python Python
from dub import Dub
with Dub(
token="DUB_API_KEY",
) as d_client:
res = d_client.analytics.retrieve(request={
"event": "clicks",
"groupBy": "regions",
"linkId": "clux0rgak00011...",
"interval": "30d",
})
print(res)
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()
->setSecurity('DUB_API_KEY')
->build();
$request = new Operations\RetrieveAnalyticsRequest(
event: 'clicks',
groupBy: 'regions',
linkId: 'clux0rgak00011...',
interval: '30d',
);
$response = $sdk->analytics->retrieve(
request: $request
);
if ($response->oneOf !== null) {
// handle response
}
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity("DUB_API_KEY"),
)
res, err := s.Analytics.Retrieve(ctx, operations.RetrieveAnalyticsRequest{
Event: dubgo.String("clicks"),
GroupBy: dubgo.String("regions"),
LinkID: dubgo.String("clux0rgak00011..."),
Interval: dubgo.String("30d"),
})
if err != nil {
log.Fatal(err)
}
if res != nil {
// handle response
}
}
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new(
security: ::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
),
)
req = ::OpenApiSDK::Operations::RetrieveAnalyticsRequest.new(
event: "clicks",
groupBy: "regions",
linkId: "clux0rgak00011...",
interval: "30d",
)
res = s.analytics.retrieve(req)
if ! res.one_of.nil?
# handle response
end
```
# Referrers data
Source: https://dub.co/docs/concepts/analytics/referrers
Analytics endpoints require a [Pro plan](https://dub.co/pricing) subscription
or higher.
Referrers data allows you to analyze where your link traffic is coming from, including both the referrer domain and the full referrer URL.
## Referrer domain analytics
The top referrers by event count, including referrer names.
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const result = await dub.analytics.retrieve({
event: "clicks",
groupBy: "referrers",
linkId: "clux0rgak00011...",
interval: "30d",
});
```
```python Python
from dub import Dub
with Dub(
token="DUB_API_KEY",
) as d_client:
res = d_client.analytics.retrieve(request={
"event": "clicks",
"groupBy": "referrers",
"linkId": "clux0rgak00011...",
"interval": "30d",
})
print(res)
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()
->setSecurity('DUB_API_KEY')
->build();
$request = new Operations\RetrieveAnalyticsRequest(
event: 'clicks',
groupBy: 'referrers',
linkId: 'clux0rgak00011...',
interval: '30d',
);
$response = $sdk->analytics->retrieve(
request: $request
);
if ($response->oneOf !== null) {
// handle response
}
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity("DUB_API_KEY"),
)
res, err := s.Analytics.Retrieve(ctx, operations.RetrieveAnalyticsRequest{
Event: dubgo.String("clicks"),
GroupBy: dubgo.String("referrers"),
LinkID: dubgo.String("clux0rgak00011..."),
Interval: dubgo.String("30d"),
})
if err != nil {
log.Fatal(err)
}
if res != nil {
// handle response
}
}
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new(
security: ::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
),
)
req = ::OpenApiSDK::Operations::RetrieveAnalyticsRequest.new(
event: "clicks",
groupBy: "referrers",
linkId: "clux0rgak00011...",
interval: "30d",
)
res = s.analytics.retrieve(req)
if ! res.one_of.nil?
# handle response
end
```
## Referrer URL analytics
The top referrer URLs by event count, including full URLs.
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const result = await dub.analytics.retrieve({
event: "clicks",
groupBy: "referer_urls",
linkId: "clux0rgak00011...",
interval: "30d",
});
```
```python Python
from dub import Dub
with Dub(
token="DUB_API_KEY",
) as d_client:
res = d_client.analytics.retrieve(request={
"event": "clicks",
"groupBy": "referer_urls",
"linkId": "clux0rgak00011...",
"interval": "30d",
})
print(res)
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()
->setSecurity('DUB_API_KEY')
->build();
$request = new Operations\RetrieveAnalyticsRequest(
event: 'clicks',
groupBy: 'referer_urls',
linkId: 'clux0rgak00011...',
interval: '30d',
);
$response = $sdk->analytics->retrieve(
request: $request
);
if ($response->oneOf !== null) {
// handle response
}
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity("DUB_API_KEY"),
)
res, err := s.Analytics.Retrieve(ctx, operations.RetrieveAnalyticsRequest{
Event: dubgo.String("clicks"),
GroupBy: dubgo.String("referer_urls"),
LinkID: dubgo.String("clux0rgak00011..."),
Interval: dubgo.String("30d"),
})
if err != nil {
log.Fatal(err)
}
if res != nil {
// handle response
}
}
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new(
security: ::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
),
)
req = ::OpenApiSDK::Operations::RetrieveAnalyticsRequest.new(
event: "clicks",
groupBy: "referer_urls",
linkId: "clux0rgak00011...",
interval: "30d",
)
res = s.analytics.retrieve(req)
if ! res.one_of.nil?
# handle response
end
```
# Tags data
Source: https://dub.co/docs/concepts/analytics/tags
Learn how to retrieve analytics data on Dub by tags
## Filter analytics by tags
You can filter analytics data by tags by passing the `tagIds` parameter to the `retrieve` function.
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const result = await dub.analytics.retrieve({
event: "clicks",
groupBy: "top_links",
tagIds: ["tag_12345", "tag_67890"],
interval: "30d",
});
```
```python Python
from dub import Dub
with Dub(
token="DUB_API_KEY",
) as d_client:
res = d_client.analytics.retrieve(request={
"event": "clicks",
"groupBy": "top_links",
"tagIds": ["tag_12345", "tag_67890"],
"interval": "30d",
})
print(res)
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()
->setSecurity('DUB_API_KEY')
->build();
$request = new Operations\RetrieveAnalyticsRequest(
event: 'clicks',
groupBy: 'top_links',
tagIds: ['tag_12345', 'tag_67890'],
interval: '30d',
);
$response = $sdk->analytics->retrieve(
request: $request
);
if ($response->oneOf !== null) {
// handle response
}
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity("DUB_API_KEY"),
)
res, err := s.Analytics.Retrieve(ctx, operations.RetrieveAnalyticsRequest{
Event: dubgo.String("clicks"),
GroupBy: dubgo.String("top_links"),
TagIDs: []string{"tag_12345", "tag_67890"},
Interval: dubgo.String("30d"),
})
if err != nil {
log.Fatal(err)
}
if res != nil {
// handle response
}
}
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new(
security: ::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
),
)
req = ::OpenApiSDK::Operations::RetrieveAnalyticsRequest.new(
event: "clicks",
groupBy: "top_links",
tagIds: ["tag_12345", "tag_67890"],
interval: "30d",
)
res = s.analytics.retrieve(req)
if ! res.one_of.nil?
# handle response
end
```
## Top tags by event
This feature is coming soon. If you'd like early access, please [contact
us](https://dub.co/contact/support).
## Tag analytics
The top links by event count, filtered by tags.
# UTM data
Source: https://dub.co/docs/concepts/analytics/utm
Analytics endpoints require a [Pro plan](https://dub.co/pricing) subscription
or higher.
UTM data allows you to analyze the effectiveness of your marketing campaigns by tracking the source, medium, campaign, term, and content parameters.
## UTM source analytics
The top UTM sources by event count.
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const result = await dub.analytics.retrieve({
event: "clicks",
groupBy: "utm_sources",
linkId: "clux0rgak00011...",
interval: "30d",
});
```
```python Python
from dub import Dub
with Dub(
token="DUB_API_KEY",
) as d_client:
res = d_client.analytics.retrieve(request={
"event": "clicks",
"groupBy": "utm_sources",
"linkId": "clux0rgak00011...",
"interval": "30d",
})
print(res)
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()
->setSecurity('DUB_API_KEY')
->build();
$request = new Operations\RetrieveAnalyticsRequest(
event: 'clicks',
groupBy: 'utm_sources',
linkId: 'clux0rgak00011...',
interval: '30d',
);
$response = $sdk->analytics->retrieve(
request: $request
);
if ($response->oneOf !== null) {
// handle response
}
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity("DUB_API_KEY"),
)
res, err := s.Analytics.Retrieve(ctx, operations.RetrieveAnalyticsRequest{
Event: dubgo.String("clicks"),
GroupBy: dubgo.String("utm_sources"),
LinkID: dubgo.String("clux0rgak00011..."),
Interval: dubgo.String("30d"),
})
if err != nil {
log.Fatal(err)
}
if res != nil {
// handle response
}
}
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new(
security: ::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
),
)
req = ::OpenApiSDK::Operations::RetrieveAnalyticsRequest.new(
event: "clicks",
groupBy: "utm_sources",
linkId: "clux0rgak00011...",
interval: "30d",
)
res = s.analytics.retrieve(req)
if ! res.one_of.nil?
# handle response
end
```
## UTM medium analytics
The top UTM mediums by event count.
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const result = await dub.analytics.retrieve({
event: "clicks",
groupBy: "utm_mediums",
linkId: "clux0rgak00011...",
interval: "30d",
});
```
```python Python
from dub import Dub
with Dub(
token="DUB_API_KEY",
) as d_client:
res = d_client.analytics.retrieve(request={
"event": "clicks",
"groupBy": "utm_mediums",
"linkId": "clux0rgak00011...",
"interval": "30d",
})
print(res)
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()
->setSecurity('DUB_API_KEY')
->build();
$request = new Operations\RetrieveAnalyticsRequest(
event: 'clicks',
groupBy: 'utm_mediums',
linkId: 'clux0rgak00011...',
interval: '30d',
);
$response = $sdk->analytics->retrieve(
request: $request
);
if ($response->oneOf !== null) {
// handle response
}
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity("DUB_API_KEY"),
)
res, err := s.Analytics.Retrieve(ctx, operations.RetrieveAnalyticsRequest{
Event: dubgo.String("clicks"),
GroupBy: dubgo.String("utm_mediums"),
LinkID: dubgo.String("clux0rgak00011..."),
Interval: dubgo.String("30d"),
})
if err != nil {
log.Fatal(err)
}
if res != nil {
// handle response
}
}
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new(
security: ::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
),
)
req = ::OpenApiSDK::Operations::RetrieveAnalyticsRequest.new(
event: "clicks",
groupBy: "utm_mediums",
linkId: "clux0rgak00011...",
interval: "30d",
)
res = s.analytics.retrieve(req)
if ! res.one_of.nil?
# handle response
end
```
## UTM campaign analytics
The top UTM campaigns by event count.
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const result = await dub.analytics.retrieve({
event: "clicks",
groupBy: "utm_campaigns",
linkId: "clux0rgak00011...",
interval: "30d",
});
```
```python Python
from dub import Dub
with Dub(
token="DUB_API_KEY",
) as d_client:
res = d_client.analytics.retrieve(request={
"event": "clicks",
"groupBy": "utm_campaigns",
"linkId": "clux0rgak00011...",
"interval": "30d",
})
print(res)
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()
->setSecurity('DUB_API_KEY')
->build();
$request = new Operations\RetrieveAnalyticsRequest(
event: 'clicks',
groupBy: 'utm_campaigns',
linkId: 'clux0rgak00011...',
interval: '30d',
);
$response = $sdk->analytics->retrieve(
request: $request
);
if ($response->oneOf !== null) {
// handle response
}
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity("DUB_API_KEY"),
)
res, err := s.Analytics.Retrieve(ctx, operations.RetrieveAnalyticsRequest{
Event: dubgo.String("clicks"),
GroupBy: dubgo.String("utm_campaigns"),
LinkID: dubgo.String("clux0rgak00011..."),
Interval: dubgo.String("30d"),
})
if err != nil {
log.Fatal(err)
}
if res != nil {
// handle response
}
}
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new(
security: ::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
),
)
req = ::OpenApiSDK::Operations::RetrieveAnalyticsRequest.new(
event: "clicks",
groupBy: "utm_campaigns",
linkId: "clux0rgak00011...",
interval: "30d",
)
res = s.analytics.retrieve(req)
if ! res.one_of.nil?
# handle response
end
```
## UTM term analytics
The top UTM terms by event count.
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const result = await dub.analytics.retrieve({
event: "clicks",
groupBy: "utm_terms",
linkId: "clux0rgak00011...",
interval: "30d",
});
```
```python Python
from dub import Dub
with Dub(
token="DUB_API_KEY",
) as d_client:
res = d_client.analytics.retrieve(request={
"event": "clicks",
"groupBy": "utm_terms",
"linkId": "clux0rgak00011...",
"interval": "30d",
})
print(res)
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()
->setSecurity('DUB_API_KEY')
->build();
$request = new Operations\RetrieveAnalyticsRequest(
event: 'clicks',
groupBy: 'utm_terms',
linkId: 'clux0rgak00011...',
interval: '30d',
);
$response = $sdk->analytics->retrieve(
request: $request
);
if ($response->oneOf !== null) {
// handle response
}
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity("DUB_API_KEY"),
)
res, err := s.Analytics.Retrieve(ctx, operations.RetrieveAnalyticsRequest{
Event: dubgo.String("clicks"),
GroupBy: dubgo.String("utm_terms"),
LinkID: dubgo.String("clux0rgak00011..."),
Interval: dubgo.String("30d"),
})
if err != nil {
log.Fatal(err)
}
if res != nil {
// handle response
}
}
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new(
security: ::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
),
)
req = ::OpenApiSDK::Operations::RetrieveAnalyticsRequest.new(
event: "clicks",
groupBy: "utm_terms",
linkId: "clux0rgak00011...",
interval: "30d",
)
res = s.analytics.retrieve(req)
if ! res.one_of.nil?
# handle response
end
```
## UTM content analytics
The top UTM contents by event count.
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const result = await dub.analytics.retrieve({
event: "clicks",
groupBy: "utm_contents",
linkId: "clux0rgak00011...",
interval: "30d",
});
```
```python Python
from dub import Dub
with Dub(
token="DUB_API_KEY",
) as d_client:
res = d_client.analytics.retrieve(request={
"event": "clicks",
"groupBy": "utm_contents",
"linkId": "clux0rgak00011...",
"interval": "30d",
})
print(res)
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()
->setSecurity('DUB_API_KEY')
->build();
$request = new Operations\RetrieveAnalyticsRequest(
event: 'clicks',
groupBy: 'utm_contents',
linkId: 'clux0rgak00011...',
interval: '30d',
);
$response = $sdk->analytics->retrieve(
request: $request
);
if ($response->oneOf !== null) {
// handle response
}
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity("DUB_API_KEY"),
)
res, err := s.Analytics.Retrieve(ctx, operations.RetrieveAnalyticsRequest{
Event: dubgo.String("clicks"),
GroupBy: dubgo.String("utm_contents"),
LinkID: dubgo.String("clux0rgak00011..."),
Interval: dubgo.String("30d"),
})
if err != nil {
log.Fatal(err)
}
if res != nil {
// handle response
}
}
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new(
security: ::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
),
)
req = ::OpenApiSDK::Operations::RetrieveAnalyticsRequest.new(
event: "clicks",
groupBy: "utm_contents",
linkId: "clux0rgak00011...",
interval: "30d",
)
res = s.analytics.retrieve(req)
if ! res.one_of.nil?
# handle response
end
```
# Bulk operations
Source: https://dub.co/docs/concepts/links/bulk-operations
Learn how to perform bulk operations on links.
Dub allows you to perform bulk operations on links. This is particularly useful when you need to [create](/api-reference/endpoint/bulk-create-links), [update](/api-reference/endpoint/bulk-update-links), or [delete](/api-reference/endpoint/bulk-delete-links) multiple links at once without having to make multiple API requests.
## Bulk create links
Bulk create allows you to create up to 100 links at once.
Bulk link creation does not support [custom link
previews](https://dub.co/help/article/custom-link-previews). Also, [webhook
events](/concepts/webhooks/introduction) will not be triggered when using bulk
link creation.
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const result = await dub.links.createMany([
{
url: "https://google.com",
},
{
url: "https://google.uk",
},
]);
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
"os"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity(os.Getenv("DUB_API_KEY")),
)
}
res, err := s.Links.CreateMany(ctx, []operations.RequestBody{
operations.RequestBody{
URL: "https://google.com",
},
operations.RequestBody{
URL: "https://google.uk",
},
})
```
```python Python
import os
import dub
from dub.models import operations
d = dub.Dub(
token=os.environ['DUB_API_KEY'],
)
res = dub.links.create_many(request=[
{
"url": "https://google.com",
},
{
"url": "https://google.uk",
},
])
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new
s.config_security(
::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
)
)
req = [
::OpenApiSDK::Operations::RequestBody.new(
url: "https://google.com",
),
::OpenApiSDK::Operations::RequestBody.new(
url: "https://example.uk"
),
]
res = s.links.create_many(req)
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()->setSecurity('DUB_API_KEY')->build();
$request = [
new Operations\RequestBody(
url: 'https://google.com',
),
new Operations\RequestBody(
url: 'https://google.uk',
),
];
$response = $sdk->links->createMany(
request: $request
);
```
```bash cURL
curl --request POST \
--url https://api.dub.co/links/bulk \
--header 'Authorization: Bearer ' \
--header 'Content-Type: application/json'
--data '[
{
"url": "https://google.com"
},
{
"url": "https://google.uk"
}
]'
```
Check out the [full API reference for the link bulk creation endpoint](/api-reference/endpoint/bulk-create-links).
## Bulk update links
Bulk update allows you to modify up to 100 links simultaneously **with the same data**.
Some potential use cases:
* Tagging multiple links at once
* Setting the same expiration date for multiple links
* Updating UTM parameters for multiple links
You cannot update the domain or key of a link with this endpoint. Also,
[webhook events](/concepts/webhooks/introduction) will not be triggered when
using bulk link updates
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const result = await dub.links.updateMany({
linkIds: ["clux0rgak00011...", "clux0rgak00022..."],
data: {
utm_source: "facebook",
utm_medium: "cpc",
},
});
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
"os"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity(os.Getenv("DUB_API_KEY")),
)
res, err := s.Links.UpdateMany(ctx, operations.BulkUpdateLinksRequestBody{
LinkIds: []string{
"clux0rgak00011...",
"clux0rgak00022...",
},
Data: map[string]string{
"utm_source": "facebook",
"utm_medium": "cpc",
},
})
}
```
```python Python
import os
import dub
from dub.models import operations
d = dub.Dub(
token=os.environ['DUB_API_KEY'],
)
res = dub.links.update_many(request={
"link_ids": [
"clux0rgak00011...",
"clux0rgak00022...",
],
"data": {
"utm_source": "facebook",
"utm_medium": "cpc",
},
})
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new
s.config_security(
::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
)
)
req = ::OpenApiSDK::Operations::BulkUpdateLinksRequestBody.new(
link_ids: [
"clux0rgak00011...",
"clux0rgak00022...",
],
data: {
"utm_source": "facebook",
"utm_medium": "cpc",
},
)
res = s.links.update_many(req)
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()->setSecurity('DUB_API_KEY')->build();
$response = $sdk->links->updateMany(
linkIds: [
'clux0rgak00011...',
'clux0rgak00022...',
],
data: {
"utm_source": "facebook",
"utm_medium": "cpc",
},
);
```
```bash cURL
curl --request PATCH \
--url https://api.dub.co/links/bulk \
--header 'Authorization: Bearer ' \
--header 'Content-Type: application/json' \
--data '{"link_ids": ["clux0rgak00011...", "clux0rgak00022..."], "data": {"utm_source": "facebook", "utm_medium": "cpc"}}'
```
Check out the [full API reference for the link bulk update endpoint](/api-reference/endpoint/bulk-update-links).
## Bulk delete links
With bulk delete, you can delete up to 100 links at once.
This is a destructive action and cannot be undone. Proceed with caution. Also,
[webhook events](/concepts/webhooks/introduction) will not be triggered when
using this endpoint.
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const result = await dub.links.deleteMany({
linkIds: ["clux0rgak00011...", "clux0rgak00022..."],
});
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
"os"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity(os.Getenv("DUB_API_KEY")),
)
res, err := s.Links.DeleteMany(ctx, operations.BulkDeleteLinksRequest{
LinkIds: []string{
"clux0rgak00011...",
"clux0rgak00022...",
},
})
}
```
```python Python
import os
import dub
from dub.models import operations
d = dub.Dub(
token=os.environ['DUB_API_KEY'],
)
res = dub.links.delete_many(request={
"link_ids": [
"clux0rgak00011...",
"clux0rgak00022...",
],
})
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new
s.config_security(
::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
)
)
req = ::OpenApiSDK::Operations::BulkDeleteLinksRequest.new(
link_ids: [
"clux0rgak00011...",
"clux0rgak00022...",
],
)
res = s.links.delete_many(req)
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()->setSecurity('DUB_API_KEY')->build();
$response = $sdk->links->deleteMany(
linkIds: [
'clux0rgak00011...',
'clux0rgak00022...',
]
);
```
```bash cURL
curl --request DELETE \
--url https://api.dub.co/links/bulk?linkIds=clux0rgak00011... \
--header 'Authorization: Bearer ' \
--header 'Content-Type: application/json'
```
Check out the [full API reference for the link bulk delete endpoint](/api-reference/endpoint/bulk-delete-links).
# Introduction
Source: https://dub.co/docs/concepts/links/introduction
Learn how to use Dub to programmatically create, update, and delete links at scale.
Links are the bread and butter of [Dub](https://dub.co).
Everything on Dub starts with a link. Whether you're creating:
* a handful of links for your marketing campaign
* hundreds of links for your [affiliate program](/partners/quickstart)
* thousands of links, [programmatically](/api-reference/endpoint/bulk-create-links), for your SMS campaign
In this guide, we'll cover the link model, how to create links, and more.
## The link model
The link model consists of the following properties:
| Property | Description | Example |
| ----------- | :-------------------------------------------------------- | :------------------------------ |
| `id` | The unique identifier of the link (prefixed with `link_`) | `link_eBKA4MT44XnI17hYLchkjUOd` |
| `url` | The destination URL of the link | `https://dub.co/home` |
| `shortLink` | The shortened version of the link (including https) | `https://dub.link/claim` |
| `domain` | The domain of the link | `dub.link` |
| `key` | The short link slug | `claim` |
For more advanced features like [custom link previews](https://dub.co/help/article/custom-link-previews), [conversion tracking](/conversions/quickstart), and more, see the full list of properties [here](/api-reference/endpoint/create-a-link).
You can use the various [Dub SDKs](/sdks/overview) to programmatically manage your links.
## Create a link
The `url` field, representing the destination URL, is the sole mandatory parameter required for the creation of a new short link.
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const link = await dub.links.create({
url: "https://google.com",
});
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
"os"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity(os.Getenv("DUB_API_KEY")),
)
res, err := s.Links.Create(ctx, &operations.CreateLinkRequestBody{
URL: "https://google.com",
})
if err != nil {
log.Fatal(err)
}
if res != nil {
// handle response
}
}
```
```python Python
import os
import dub
from dub.models import operations
d = dub.Dub(
token=os.environ['DUB_API_KEY'],
)
res = d.links.create(request={
"url": "https://google.com",
})
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new
s.config_security(
::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
)
)
req = ::OpenApiSDK::Operations::CreateLinkRequestBody.new(
url: "https://google.com",
)
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()->setSecurity('DUB_API_KEY')->build();
$request = new Operations\CreateLinkRequestBody(
url: 'https://google.com',
);
$response = $sdk->links->create(
request: $request
);
```
```bash cURL {5}
curl --request POST \
--url https://api.dub.co/links \
--header 'Authorization: Bearer ' \
--header 'Content-Type: application/json' \
--data '{"url": "https://google.com"}'
```
Check out the [full API reference for the link creation endpoint](/api-reference/endpoint/create-a-link).
## Update a link
An existing link can be updated by providing the `id` to the `update` method. This method returns the updated link as a response.
You can use either the `linkId` or an `externalId` prefixed with `ext_` which is a unique identifier for the link in your own database to associate it with the link in Dub's system.
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const link = await dub.links.update("link_eBKA4MT44XnI17hYLchkjUOd", {
url: "https://www.google.uk", // new URL
});
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
"os"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity(os.Getenv("DUB_API_KEY")),
)
res, err := s.Links.Update(ctx, "link_eBKA4MT44XnI17hYLchkjUOd", &operations.UpdateLinkRequestBody{
URL: "https://www.google.uk", // new URL
})
if err != nil {
log.Fatal(err)
}
if res != nil {
// handle response
}
}
```
```python Python
import os
import dub
from dub.models import operations
d = dub.Dub(
token=os.environ['DUB_API_KEY'],
)
res = dub.links.update(link_id="link_eBKA4MT44XnI17hYLchkjUOd", request_body={
"url": "https://www.google.uk", // new URL
})
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new
s.config_security(
::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
)
)
res = s.links.update(link_id="link_eBKA4MT44XnI17hYLchkjUOd", request_body={
"url": "https://www.google.uk", // new URL
})
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()->setSecurity('DUB_API_KEY')->build();
$requestBody = new Operations\UpdateLinkRequestBody(
url: 'https://www.google.uk', // new URL
);
$response = $sdk->links->update(
linkId: 'link_eBKA4MT44XnI17hYLchkjUOd',
requestBody: $requestBody
);
```
```bash cURL
curl --request PATCH \
--url https://api.dub.co/links/link_eBKA4MT44XnI17hYLchkjUOd \
--header 'Authorization: Bearer ' \
--header 'Content-Type: application/json' \
--data '{"url": "https://www.google.uk"}'
```
Check out the [full API reference for the link update endpoint](/api-reference/endpoint/update-a-link).
## Upsert a link
Upserting a link is a combination of creating and updating a link. If a link with the same URL already exists, return it (or update it if there are any changes). Otherwise, a new link will be created.
This allows you to use the upsert method without the necessity of checking for the link's existence beforehand.
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const link = await dub.links.upsert({
url: "https://google.com", // will always be the same short link
});
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
"os"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity(os.Getenv("DUB_API_KEY")),
)
res, err := s.Links.Upsert(ctx, &operations.UpsertLinkRequestBody{
URL: "https://google.com",
})
if err != nil {
log.Fatal(err)
}
if res != nil {
// handle response
}
}
```
```python Python
import os
import dub
from dub.models import operations
d = dub.Dub(
token=os.environ['DUB_API_KEY'],
)
res = d.links.upsert(request={
"url": "https://google.com",
})
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new
s.config_security(
::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
)
)
req = ::OpenApiSDK::Operations::UpsertLinkRequestBody.new(
url: "https://google.com",
)
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()->setSecurity('DUB_API_KEY')->build();
$request = new Operations\UpsertLinkRequestBody(
url: 'https://google.com',
);
$response = $sdk->links->upsert(
request: $request
);
```
```bash cURL
curl --request POST \
--url https://api.dub.co/links/upsert \
--header 'Authorization: Bearer ' \
--header 'Content-Type: application/json' \
--data '{"url": "https://google.com"}'
```
Check out the [full API reference for the link upsert endpoint](/api-reference/endpoint/upsert-a-link).
# Organizing links
Source: https://dub.co/docs/concepts/links/organization
Learn how to associate links with users, campaigns, teams, and other entities within your system.
When creating links programmatically with Dub, you might want a way to associate them with a user or other identifiers in your system.
There are a few ways to do this, depending on your data structure:
| Method | Type | Description | Use case |
| --------------------------- | ------------ | -------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| [External ID](#external-id) | One-to-one | A unique identifier for a link within your system. | Associating referral links with users in your system. |
| [Tenant ID](#tenant-id) | One-to-many | The ID of the tenant that created the link. | Grouping all links created by a user/team in your system. |
| [Tags](#tags) | Many-to-many | Grouping links by tags | Organizing links by campaign / user / various for flexible, multi-dimensional filtering and reporting |
## External ID
In certain scenarios, it is essential to identify links within your system. For instance, you may need to associate a link with a user without storing the Dub link ID directly in your database (e.g. for referral links).
The `externalId` field serves this purpose effectively. It acts as a unique identifier within your database, allowing you to associate it with a corresponding link in Dub's system.
Dub allows you to create links using an `externalId` and subsequently retrieve them by the same identifier.
`externalId` should be a unique value across your workspace. Trying to create
a link with an externalId that already exists will result in a [`409` conflict
error](/api-reference/introduction#conflict) error.
### Create link with an externalId
Here is an example of how to create a link with an `externalId`:
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const link = await dub.links.create({
url: "https://google.com",
externalId: "12345",
});
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
"os"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity(os.Getenv("DUB_API_KEY")),
)
res, err := s.Links.Create(ctx, &operations.CreateLinkRequestBody{
URL: "https://google.com",
ExternalId: "12345",
})
if err != nil {
log.Fatal(err)
}
if res != nil {
// handle response
}
}
```
```python Python
import os
import dub
from dub.models import operations
d = dub.Dub(
token=os.environ['DUB_API_KEY'],
)
res = d.links.create(request={
"url": "https://google.com",
"external_id": "12345",
})
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new
s.config_security(
::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
)
)
req = ::OpenApiSDK::Operations::CreateLinkRequestBody.new(
url: "https://google.com",
external_id: "12345",
)
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()->setSecurity('DUB_API_KEY')->build();
$request = new Operations\CreateLinkRequestBody(
url: 'https://google.com',
externalId: '12345',
);
$response = $sdk->links->create(
request: $request
);
```
```bash cURL
curl --request POST \
--url https://api.dub.co/links \
--header 'Authorization: Bearer ' \
--header 'Content-Type: application/json' \
--data '{
"url": "https://google.com",
"external_id": "12345"
}'
```
### Retrieve link by externalId
Let's see how to retrieve a link by its `externalId`:
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const link = await dub.links.get({
externalId: "12345",
});
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
"os"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity(os.Getenv("DUB_API_KEY")),
)
res, err := s.Links.Get(ctx, operations.GetLinkInfoRequest{
ExternalID: dubgo.String("12345"),
})
}
```
```python Python
import os
import dub
from dub.models import operations
d = dub.Dub(
token=os.environ['DUB_API_KEY'],
)
res = d.links.get(request={
"external_id": "12345",
})
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new
s.config_security(
::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
)
)
req = ::OpenApiSDK::Operations::GetLinkInfoRequest.new(
external_id: "12345",
)
res = s.links.get(req)
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()->setSecurity('DUB_API_KEY')->build();
$response = $sdk->links->get(
externalId: '12345'
);
```
```bash cURL
curl --request GET \
--url https://api.dub.co/links/info?external_id=12345 \
--header 'Authorization: Bearer ' \
--header 'Content-Type: application/json'
```
### Update link by externalId
In addition to updating a link by its `linkId`, you can also update a link by its `externalId`.
Make sure to prefix the `externalId` with `ext_`. For example, if your
`externalId` is `12345`, you should pass `ext_12345`.
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const link = await dub.links.update("ext_12345", {
url: "https://www.google.uk", // new URL
});
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
"os"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity(os.Getenv("DUB_API_KEY")),
)
res, err := s.Links.Update(ctx, "ext_12345", &operations.UpdateLinkRequestBody{
URL: "https://www.google.uk", // new URL
})
if err != nil {
log.Fatal(err)
}
if res != nil {
// handle response
}
}
```
```python Python
import os
import dub
from dub.models import operations
d = dub.Dub(
token=os.environ['DUB_API_KEY'],
)
res = dub.links.update(link_id="ext_12345", request_body={
"url": "https://www.google.uk", // new URL
})
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new
s.config_security(
::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
)
)
res = s.links.update(link_id="ext_12345", request_body={
"url": "https://www.google.uk", // new URL
})
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()->setSecurity('DUB_API_KEY')->build();
$requestBody = new Operations\UpdateLinkRequestBody(
url: 'https://www.google.uk', // new URL
);
$response = $sdk->links->update(
linkId: 'ext_12345',
requestBody: $requestBody
);
```
```bash cURL
curl --request PATCH \
--url https://api.dub.co/links/ext_12345 \
--header 'Authorization: Bearer ' \
--header 'Content-Type: application/json' \
--data '{"url": "https://www.google.uk"}'
```
### Retrieve analytics by externalId
You can also retrieve analytics for a link by its `externalId`. This is helpful for fetching the analytics for a given link using the unique identifier from your system.
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const analytics = await dub.analytics.retrieve({
externalId: "ext_12345",
});
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
"os"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity(os.Getenv("DUB_API_KEY")),
)
res, err := s.Analytics.Retrieve(ctx, operations.RetrieveAnalyticsRequest{
ExternalID: dubgo.String("ext_12345"),
})
}
```
```python Python
import os
import dub
from dub.models import operations
d = dub.Dub(
token=os.environ['DUB_API_KEY'],
)
res = dub.analytics.retrieve(request={
"external_id": "ext_12345",
})
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new
s.config_security(
::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
)
)
req = ::OpenApiSDK::Operations::RetrieveAnalyticsRequest.new(
external_id: "ext_12345",
)
res = s.analytics.retrieve(req)
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()->setSecurity('DUB_API_KEY')->build();
$request = new Operations\RetrieveAnalyticsRequest(
externalId: "ext_12345",
);
$response = $sdk->analytics->retrieve(
request: $request
);
```
```bash cURL
curl --request GET \
--url https://api.dub.co/analytics?external_id=ext_12345 \
--header 'Authorization: Bearer ' \
--header 'Content-Type: application/json'
```
## Tenant ID
The ID of the tenant that created the link inside your system. If set, it can be used to fetch all links for a tenant. This is useful if you have a system that lets your users create their own links, and you want to group them on a tenant level.
### Create link with tenantId
Let's see how to create a link with a tenant ID:
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const link = await dub.links.create({
url: "https://google.com",
tenantId: "12345",
});
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
"os"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity(os.Getenv("DUB_API_KEY")),
)
res, err := s.Links.Create(ctx, &operations.CreateLinkRequestBody{
URL: "https://google.com",
TenantId: "12345",
})
if err != nil {
log.Fatal(err)
}
if res != nil {
// handle response
}
}
```
```python Python
import os
import dub
from dub.models import operations
d = dub.Dub(
token=os.environ['DUB_API_KEY'],
)
res = d.links.create(request={
"url": "https://google.com",
"tenant_id": "12345",
})
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new
s.config_security(
::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
)
)
req = ::OpenApiSDK::Operations::CreateLinkRequestBody.new(
url: "https://google.com",
tenant_id: "12345",
)
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()->setSecurity('DUB_API_KEY')->build();
$request = new Operations\CreateLinkRequestBody(
url: 'https://google.com',
tenantId: '12345',
);
$response = $sdk->links->create(
request: $request
);
```
```bash cURL
curl --request POST \
--url https://api.dub.co/links \
--header 'Authorization: Bearer ' \
--header 'Content-Type: application/json' \
--data '{
"url": "https://google.com",
"external_id": "12345"
}'
```
### Retrieve links by tenantId
Here is how to retrieve links by tenant ID:
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const result = await dub.links.list({
tenantId: "12345",
});
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
"os"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity(os.Getenv("DUB_API_KEY")),
)
res, err := s.Links.List(ctx, operations.GetLinksRequest{
TenantId: dubgo.String("12345"),
})
}
```
```python Python
import os
import dub
from dub.models import operations
d = dub.Dub(
token=os.environ['DUB_API_KEY'],
)
res = s.links.list(request={
"tenant_id": "12345",
})
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new
s.config_security(
::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
)
)
req = ::OpenApiSDK::Operations::GetLinksRequest.new(
tenant_id: "12345",
)
res = s.links.list(req)
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()->setSecurity('DUB_API_KEY')->build();
$request = new Operations\GetLinksRequest(
tenantId: "12345",
);
$responses = $sdk->links->list(
request: $request
);
```
```bash cURL
curl --request GET \
--url https://api.dub.co/links?tenantId=12345 \
--header 'Authorization: Bearer ' \
--header 'Content-Type: application/json'
```
### Retrieve analytics by tenantId
You can retrieve analytics by tenantId by passing the `tenantId` prop. This is helpful for fetching the analytics for all the links under a specific tenant, or the total analytics for a tenant.
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const analytics = await dub.analytics.retrieve({
tenantId: "12345",
});
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
"os"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity(os.Getenv("DUB_API_KEY")),
)
res, err := s.Analytics.Retrieve(ctx, operations.RetrieveAnalyticsRequest{
TenantId: dubgo.String("12345"),
})
}
```
```python Python
import os
import dub
from dub.models import operations
d = dub.Dub(
token=os.environ['DUB_API_KEY'],
)
res = dub.analytics.retrieve(request={
"tenant_id": "12345",
})
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new
s.config_security(
::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
)
)
req = ::OpenApiSDK::Operations::RetrieveAnalyticsRequest.new(
tenant_id: "12345",
)
res = s.analytics.retrieve(req)
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()->setSecurity('DUB_API_KEY')->build();
$request = new Operations\RetrieveAnalyticsRequest(
tenantId: "12345",
);
$response = $sdk->analytics->retrieve(
request: $request
);
```
```bash cURL
curl --request GET \
--url https://api.dub.co/analytics?tenantId=12345 \
--header 'Authorization: Bearer ' \
--header 'Content-Type: application/json'
```
***
## Tags
Tags are a great way to organize your links on Dub.
With tags, you can:
* Organize your links by campaigns, clients, or any other categories you can think of.
* [Filter your links by tags](#retrieve-links-by-tags) and get a shareable link to the filtered results.
* [Filter your analytics by tags](#retrieve-analytics-by-tags) to see how your campaigns are performing.
### Create link with tags
You can use either pass the tag ID or tag name to create a link with tags.
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const link = await dub.links.create({
url: "https://example.com",
tagIds: ["clux0rgak00011..."],
});
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
"os"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity(os.Getenv("DUB_API_KEY")),
)
res, err := s.Links.Create(ctx, &operations.CreateLinkRequestBody{
URL: "https://example.com",
TagIds: []string{"clux0rgak00011..."},
})
if err != nil {
log.Fatal(err)
}
if res != nil {
// handle response
}
}
```
```python Python
import os
import dub
from dub.models import operations
d = dub.Dub(
token=os.environ['DUB_API_KEY'],
)
res = d.links.create(request={
"url": "https://example.com",
"tag_ids": ["clux0rgak00011..."],
})
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new
s.config_security(
::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
)
)
req = ::OpenApiSDK::Operations::CreateLinkRequestBody.new(
url: "https://example.com",
tag_ids: ["clux0rgak00011..."],
)
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()->setSecurity('DUB_API_KEY')->build();
$request = new Operations\CreateLinkRequestBody(
url: 'https://example.com',
tagIds: ['clux0rgak00011...'],
);
$response = $sdk->links->create(
request: $request
);
```
```bash cURL
curl --request POST \
--url https://api.dub.co/links \
--header 'Authorization: Bearer ' \
--header 'Content-Type: application/json' \
--data '{
"url": "https://example.com",
"tagIds": ["clux0rgak00011..."]
}'
```
### Retrieve links by tags
You can retrieve links by tag by tags.
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const result = await dub.links.list({
tagNames: ["tag1"],
});
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
"os"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity(os.Getenv("DUB_API_KEY")),
)
res, err := s.Links.List(ctx, operations.GetLinksRequest{
TagNames: []string{"tag1"},
})
}
```
```python Python
import os
import dub
from dub.models import operations
d = dub.Dub(
token=os.environ['DUB_API_KEY'],
)
res = s.links.list(request={
"tag_names": ["tag1"],
})
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new
s.config_security(
::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
)
)
req = ::OpenApiSDK::Operations::GetLinksRequest.new(
tag_names: ["tag1"],
)
res = s.links.list(req)
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()->setSecurity('DUB_API_KEY')->build();
$request = new Operations\GetLinksRequest(
tagNames: ["tag1"],
);
$responses = $sdk->links->list(
request: $request
);
```
```bash cURL
curl --request GET \
--url https://api.dub.co/links?tagNames=tag1 \
--header 'Authorization: Bearer ' \
--header 'Content-Type: application/json'
```
### Retrieve analytics by tags
You can retrieve analytics for a tag (or multiple tags) by passing the `tagIds` prop. This is helpful for fetching the analytics for all the links under a specific tag, or the total analytics for a tag (or multiple tags).
```javascript Node.js
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const analytics = await dub.analytics.retrieve({
tagIds: ["tag_xxx"],
});
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
"os"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity(os.Getenv("DUB_API_KEY")),
)
res, err := s.Analytics.Retrieve(ctx, operations.RetrieveAnalyticsRequest{
TagIds: []string{"tag_xxx"},
})
}
```
```python Python
import os
import dub
from dub.models import operations
d = dub.Dub(
token=os.environ['DUB_API_KEY'],
)
res = dub.analytics.retrieve(request={
"tag_ids": ["tag_xxx"],
})
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new
s.config_security(
::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
)
)
req = ::OpenApiSDK::Operations::RetrieveAnalyticsRequest.new(
tag_ids: ["tag_xxx"],
)
res = s.analytics.retrieve(req)
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()->setSecurity('DUB_API_KEY')->build();
$request = new Operations\RetrieveAnalyticsRequest(
tagIds: ["tag_xxx"],
);
$response = $sdk->analytics->retrieve(
request: $request
);
```
```bash cURL
curl --request GET \
--url https://api.dub.co/analytics?tagIds=tag_xxx \
--header 'Authorization: Bearer ' \
--header 'Content-Type: application/json'
```
# Event types
Source: https://dub.co/docs/concepts/webhooks/event-types
List of available webhook events you can listen to along with their payload examples
Webhooks are a great way to get real-time notifications on events that happen in your Dub workspace. Webhooks on Dub follow the following format:
```json webhook-payload.json
{
"id": "evt_KleiO4HBwZFbO1vZLWIPZ2AtX", // The event ID
"event": "link.created", // The event type
"createdAt": "2024-08-26T16:41:52.346Z", // The timestamp of when the event was created
"data": {
// Event payload
}
}
```
There are two types of webhook events you can listen to:
* [**Workspace-level events**](#workspace-level-events)
* [**Link-level events**](#link-level-events)
## Workspace-level events
These events are triggered in the context of your entire workspace:
* [`link.created`](#link-created)
* [`link.updated`](#link-updated)
* [`link.deleted`](#link-deleted)
* [`lead.created`](#lead-created)
* [`sale.created`](#sale-created)
* [`partner.enrolled`](#partner-enrolled)
### `link.created`
This event is triggered when a [new link is created](/api-reference/endpoint/create-a-link) in your Dub workspace. The event payload contains the created link's details.
Here's an example payload:
```json link.created
{
"id": "evt_KleiO4HBwZFbO1vZLWIPZ2AtX",
"event": "link.created",
"createdAt": "2024-08-26T16:41:52.346Z",
"data": {
"id": "cm0b87844000dismqhkviju54",
"domain": "dub.sh",
"key": "sOvvXDT",
"externalId": null,
"url": "https://github.com/stack-auth/stack",
"trackConversion": false,
"archived": false,
"expiresAt": null,
"expiredUrl": null,
"password": null,
"proxy": false,
"title": null,
"description": null,
"image": null,
"video": null,
"rewrite": false,
"doIndex": false,
"ios": null,
"android": null,
"geo": null,
"publicStats": false,
"tagId": null,
"tags": [],
"comments": null,
"shortLink": "https://dub.sh/sOvvXDT",
"qrCode": "https://api.dub.co/qr?url=https://dub.sh/sOvvXDT?qr=1",
"utm_source": null,
"utm_medium": null,
"utm_campaign": null,
"utm_term": null,
"utm_content": null,
"userId": "cm022rkcw0000ikt14mscg9sg",
"workspaceId": "ws_cm022sis60003ikt1syy7kfhl",
"clicks": 0,
"lastClicked": null,
"leads": 0,
"sales": 0,
"saleAmount": 0,
"createdAt": "2024-08-26T16:41:52.084Z",
"updatedAt": "2024-08-26T16:41:52.084Z",
"projectId": "cm022sis60003ikt1syy7kfhl"
}
}
```
### `link.updated`
This event is triggered when a [link is updated](/api-reference/endpoint/update-a-link) in your Dub workspace. The event payload contains the updated link's details.
Here's an example payload:
```json link.updated
{
"id": "event_KleiO4HBwZFbO1vZLWIPZ2AtX",
"event": "link.updated",
"createdAt": "2024-08-26T16:41:52.346Z",
"data": {
"id": "cm0b87844000dismqhkviju54",
"domain": "dub.sh",
"key": "sOvvXDT",
"externalId": null,
"url": "https://github.com/stack-auth/stack",
"trackConversion": false,
"archived": false,
"expiresAt": null,
"expiredUrl": null,
"password": null,
"proxy": false,
"title": null,
"description": null,
"image": null,
"video": null,
"rewrite": false,
"doIndex": false,
"ios": null,
"android": null,
"geo": null,
"publicStats": false,
"tagId": null,
"tags": [],
"comments": null,
"shortLink": "https://dub.sh/sOvvXDT",
"qrCode": "https://api.dub.co/qr?url=https://dub.sh/sOvvXDT?qr=1",
"utm_source": null,
"utm_medium": null,
"utm_campaign": null,
"utm_term": null,
"utm_content": null,
"userId": "cm022rkcw0000ikt14mscg9sg",
"workspaceId": "ws_cm022sis60003ikt1syy7kfhl",
"clicks": 0,
"lastClicked": null,
"leads": 0,
"sales": 0,
"saleAmount": 0,
"createdAt": "2024-08-26T16:41:52.084Z",
"updatedAt": "2024-08-26T16:41:52.084Z",
"projectId": "cm022sis60003ikt1syy7kfhl"
}
}
```
### `link.deleted`
This event is triggered when a [link is deleted](/api-reference/endpoint/delete-a-link) in your Dub workspace. The event payload contains the deleted link's details.
Here's an example payload:
```json link.deleted
{
"id": "evt_KleiO4HBwZFbO1vZLWIPZ2AtX",
"event": "link.deleted",
"createdAt": "2024-08-26T16:41:52.346Z",
"data": {
"id": "cm0b87844000dismqhkviju54",
"domain": "dub.sh",
"key": "sOvvXDT",
"externalId": null,
"url": "https://github.com/stack-auth/stack",
"trackConversion": false,
"archived": false,
"expiresAt": null,
"expiredUrl": null,
"password": null,
"proxy": false,
"title": null,
"description": null,
"image": null,
"video": null,
"rewrite": false,
"doIndex": false,
"ios": null,
"android": null,
"geo": null,
"publicStats": false,
"tagId": null,
"tags": [],
"comments": null,
"shortLink": "https://dub.sh/sOvvXDT",
"qrCode": "https://api.dub.co/qr?url=https://dub.sh/sOvvXDT?qr=1",
"utm_source": null,
"utm_medium": null,
"utm_campaign": null,
"utm_term": null,
"utm_content": null,
"userId": "cm022rkcw0000ikt14mscg9sg",
"workspaceId": "ws_cm022sis60003ikt1syy7kfhl",
"clicks": 0,
"lastClicked": null,
"leads": 0,
"sales": 0,
"saleAmount": 0,
"createdAt": "2024-08-26T16:41:52.084Z",
"updatedAt": "2024-08-26T16:41:52.084Z",
"projectId": "cm022sis60003ikt1syy7kfhl"
}
}
```
### `lead.created`
This event is triggered when a [new lead is created](/api-reference/endpoint/track-lead) via [Dub Conversions](/conversions/quickstart). The event payload contains the following:
* `eventName`: The name of the event that was tracked.
* `customer`: Details about the customer that signed up.
* `click`: Details about the click event that led to the lead event.
* `link`: Details about the referral link that the lead event is associated with.
Here's an example payload:
```json lead.created
{
"id": "evt_P343bmyae40ALQYr5HT4vRXRd",
"event": "lead.created",
"createdAt": "2024-08-30T09:53:50.343Z",
"data": {
"eventName": "Sign up",
"customer": {
"id": "oU5P0SqI8fpwx5bxw1",
"name": "John",
"email": "john@example.com",
"avatar": "https://example.com/john.jpeg"
},
"click": {
"id": "d0UtZqE0BZuBPrJS",
"url": "https://github.com/dubinc/dub",
"ip": "63.141.57.109",
"continent": "NA",
"country": "US",
"city": "San Francisco",
"device": "Desktop",
"browser": "Chrome",
"os": "Mac OS",
"ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36",
"bot": false,
"qr": false,
"referer": "(direct)"
},
"link": {
"id": "cm0faqkyn0001txvfwjfeq7gl",
"domain": "dub.sh",
"key": "79ys3WA",
"externalId": null,
"url": "https://github.com/dubinc/dub",
"trackConversion": true,
"archived": false,
"expiresAt": null,
"expiredUrl": null,
"password": null,
"proxy": false,
"title": null,
"description": null,
"image": null,
"video": null,
"rewrite": false,
"doIndex": false,
"ios": null,
"android": null,
"geo": null,
"publicStats": false,
"comments": null,
"shortLink": "https://dub.sh/79ys3WA",
"qrCode": "https://api.dub.co/qr?url=https://dub.sh/79ys3WA?qr=1",
"utm_source": null,
"utm_medium": null,
"utm_campaign": null,
"utm_term": null,
"utm_content": null,
"userId": "cm022rkcw0000ikt14mscg9sg",
"workspaceId": "ws_cm022sis60003ikt1syy7kfhl",
"clicks": 10,
"lastClicked": "2024-08-30T07:45:09.000Z",
"leads": 5,
"sales": 0,
"saleAmount": 0,
"createdAt": "2024-08-29T13:03:59.098Z",
"updatedAt": "2024-08-30T09:53:49.505Z"
}
}
}
```
### `sale.created`
This event is triggered when a [new sale is tracked](/api-reference/endpoint/track-sale) via [Dub Conversions](/conversions/quickstart). The event payload contains the following:
* `eventName`: The name of the event that was tracked.
* `customer`: Details about the customer that made the purchase.
* `click`: Details about the click event that led to the sale event.
* `link`: Details about the referral link that the sale event is associated with.
Here's an example payload:
```json sale.created
{
"id": "evt_WHjyHhqsfYOrlJOOVJSoHXysD",
"event": "sale.created",
"createdAt": "2024-08-30T09:57:51.245Z",
"data": {
"eventName": "Purchased",
"customer": {
"id": "cm0gjdvr20001dkbha2n9gt2b",
"name": "John",
"email": "john@example.com",
"avatar": "https://example.com/john.jpeg"
},
"click": {
"id": "d0UtZqE0BZuBPrJS",
"url": "https://github.com/dubinc/dub",
"ip": "63.141.57.109",
"continent": "NA",
"country": "US",
"city": "San Francisco",
"device": "Desktop",
"browser": "Chrome",
"os": "Mac OS",
"ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36",
"bot": false,
"qr": false,
"referer": "(direct)"
},
"link": {
"id": "cm0faqkyn0001txvfwjfeq7gl",
"domain": "dub.sh",
"key": "79ys3WA",
"externalId": null,
"url": "https://github.com/dubinc/dub",
"trackConversion": true,
"archived": false,
"expiresAt": null,
"expiredUrl": null,
"password": null,
"proxy": false,
"title": null,
"description": null,
"image": null,
"video": null,
"rewrite": false,
"doIndex": false,
"ios": null,
"android": null,
"geo": null,
"publicStats": false,
"comments": null,
"shortLink": "https://dub.sh/79ys3WA",
"qrCode": "https://api.dub.co/qr?url=https://dub.sh/79ys3WA?qr=1",
"utm_source": null,
"utm_medium": null,
"utm_campaign": null,
"utm_term": null,
"utm_content": null,
"userId": "cm022rkcw0000ikt14mscg9sg",
"workspaceId": "ws_cm022sis60003ikt1syy7kfhl",
"clicks": 10,
"lastClicked": "2024-08-30T07:45:09.000Z",
"leads": 5,
"sales": 1,
"saleAmount": 20000,
"createdAt": "2024-08-29T13:03:59.098Z",
"updatedAt": "2024-08-30T09:57:50.891Z"
},
"sale": {
"amount": 4500,
"currency": "usd",
"paymentProcessor": "stripe",
"invoiceId": null
}
}
}
```
### `partner.enrolled`
This event is triggered when a [new partner is enrolled](/api-reference/endpoint/create-a-partner) in your partner program. The event payload contains the following:
* `partner`: Details about the partner that was enrolled.
* `links`: An array of the partner's referral links.
Here's an example payload:
```json partner.enrolled
{
"id": "evt_ovabfqva8oqZzmLPN1JnwIfdt",
"event": "partner.enrolled",
"createdAt": "2025-04-08T17:11:56.492Z",
"data": {
"id": "pn_1JRB6678XHGBZE95R5PH5QVGS",
"name": "Asleep Pink Mammal",
"email": "chosen.blush.barracuda@dub-internal-test.com",
"image": "https://api.dub.co/og/avatar?seed=Asleep Pink Mammal",
"description": null,
"country": "US",
"payoutsEnabledAt": null,
"paypalEmail": null,
"stripeConnectId": null,
"createdAt": "2025-04-08T17:11:56.446Z",
"status": "approved",
"programId": "prog_CYCu7IMAapjkRpTnr8F1azjN",
"tenantId": null,
"clicks": 0,
"leads": 0,
"sales": 0,
"saleAmount": 0,
"earnings": 0,
"applicationId": null,
"website": "https://example.com",
"youtube": null,
"twitter": null,
"linkedin": null,
"instagram": null,
"tiktok": null,
"links": [
{
"id": "link_1JRB6677YXQB49RC1HKH7TPJE",
"domain": "getacme.link",
"key": "uvYO5pMIpctKdUVJlL3jIL4o",
"shortLink": "https://getacme.link/uvYO5pMIpctKdUVJlL3jIL4o",
"url": "https://acme.com",
"clicks": 0,
"leads": 0,
"sales": 0,
"saleAmount": 0
}
]
}
}
```
## Link-level events
Due to the high volume nature of these events, these events are scoped to a specific link.
This means that you need to specify the link when creating a webhook – though you can select multiple links for the same webhook if you'd like.
### `link.clicked`
This event is triggered when a user clicks on a link. The event payload contains all the details about the click event.
Here's an example payload:
```json link.clicked
{
"id": "evt_b9ywgxWqai2glUpCQjclB17kM",
"event": "link.clicked",
"createdAt": "2024-08-30T10:16:13.149Z",
"data": {
"click": {
"timestamp": "2024-08-30T10:16:12.124Z",
"clickId": "d0UtZqE0BZuBPrJS",
"url": "https://github.com/dubinc/dub",
"ip": "63.141.57.109",
"continent": "NA",
"country": "US",
"city": "San Francisco",
"device": "Desktop",
"browser": "Chrome",
"os": "Mac OS",
"ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36",
"bot": false,
"qr": false,
"referer": "(direct)"
},
"link": {
"id": "cm0faqkyn0001txvfwjfeq7gl",
"domain": "dub.sh",
"key": "79ys3WA",
"externalId": null,
"url": "https://github.com/dubinc/dub",
"trackConversion": true,
"archived": false,
"expiresAt": null,
"expiredUrl": null,
"password": null,
"proxy": false,
"title": null,
"description": null,
"image": null,
"video": null,
"rewrite": false,
"doIndex": false,
"ios": null,
"android": null,
"geo": null,
"publicStats": false,
"comments": null,
"shortLink": "https://dub.sh/79ys3WA",
"qrCode": "https://api.dub.co/qr?url=https://dub.sh/79ys3WA?qr=1",
"utm_source": null,
"utm_medium": null,
"utm_campaign": null,
"utm_term": null,
"utm_content": null,
"userId": "cm022rkcw0000ikt14mscg9sg",
"workspaceId": "ws_cm022sis60003ikt1syy7kfhl",
"clicks": 11,
"lastClicked": "2024-08-30T07:45:09.000Z",
"leads": 6,
"sales": 10,
"saleAmount": 200000,
"createdAt": "2024-08-29T13:03:59.098Z",
"updatedAt": "2024-08-30T10:16:12.126Z"
}
}
}
```
# Introduction
Source: https://dub.co/docs/concepts/webhooks/introduction
Use webhooks to get real-time notifications on events happening across your Dub workspace.
Webhooks allows you to listen to real-time events happening across your Dub workspace. With webhooks, you can build custom integrations with Dub, such as:
* Triggering a Zap on [Zapier](https://dub.co/integrations/zapier) when a new link is created in Dub
* Sending click events in real-time to [Segment](https://dub.co/integrations/segment) for further processing
* Get a [Slack](https://dub.co/integrations/slack) notification when someone clicks on your pitch deck link
* Building gamified referral programs with [Dub Conversions](/conversions/quickstart) – e.g. increment usage credits for the referrer when a [new signup](/conversions/leads) happens
The following endpoints do not trigger webhook events: [Bulk create
links](/api-reference/endpoint/bulk-create-links), [Bulk update
links](/api-reference/endpoint/bulk-update-links), [Bulk delete
links](/api-reference/endpoint/bulk-delete-links).
In this guide, we'll show you how to configure webhooks for your Dub workspace and a list of available events you can listen to.
## Creating a webhook
To create a webhook for your Dub workspace, you'll need to follow these steps:
Navigate to the [**Webhooks** settings page](https://app.dub.co/webhooks) in your Dub workspace.
Click on **Create Webhook** to create a new webhook.
Fill in the required fields in the webhook creation form:
1. **Name**: Give your webhook a name that helps you identify it.
2. **URL**: Enter the URL of the endpoint where you want to send the webhook. We recommend using [webhook.site](https://webhook.site/) to test your webhook.
3. **Signing secret**: This is an auto-generated secret key that you can use to verify the authenticity of the webhook in your application. Learn more about [verifying webhook requests](/concepts/webhooks/verify-webhook-requests).
4. **Events**: Select the events you want to listen to. You can select multiple events. Refer to the [Event Types](/concepts/webhooks/event-types) section to see the list of available events.
Finally, click on **Create webhook** to create the webhook.
## Viewing webhook event logs
We also provide you with a webhook event logs page where you can view all the webhook events that have been sent to your webhook endpoint in real-time.
To view the webhook event logs, select the webhook from the [**Webhooks** settings page](https://app.dub.co/webhooks) and click on the **Webhook Logs** tab.
Here, you'll see a list of all the webhook events that have been sent to your webhook endpoint:
You can also select on a specific event, which will open up a sheet with more details about the event:
## Sending test events
You can send test events to your webhook URL to ensure that it's working correctly. To do this:
Navigate to the [**Webhooks** settings page](https://app.dub.co/webhooks) and select the webhook you want to test.
Click on the **Update Details** tab to open the webhook details page.
Select the `⋮` icon on the top right of the page, and click on **Send test event**.
This will open up a modal where you can select the event you want to send.
Select the event you want to send, and click on **Send test webhook**.
You'll see a success message and receive the webhook event in the webhook endpoint you specified.
## Retry Behaviour
If your webhook endpoint does not respond with a success status code (2XX), we retry the request to ensure every message will be delivered. You can see all the retry attempts in your webhook event logs.
Webhooks are retried until they are successfully delivered – with an exponential backoff to avoid overwhelming your webhook endpoint (also known as the "[thundering herd problem](https://en.wikipedia.org/wiki/Thundering_herd_problem)"). The delay is capped at 24 hours from the 5th retry attempt onwards.
| Retry attempt | Delay |
| ------------- | -------- |
| 1st | 12s |
| 2nd | 2m 28s |
| 3rd | 30m 8s |
| 4th | 6h 7m 6s |
| 5th | 24h |
| 6th | 24h |
| ... | ... |
### Temporary Disablement
If a webhook endpoint consistently fails, it will be automatically disabled after a series of failed attempts.
Notifications will be sent to the Workspace owners at the following intervals:
* After 5, 10, and 15 consecutive failed attempts.
* On the 20th consecutive failed attempt, the **webhook will be disabled**.
This mechanism ensures that non-responsive endpoints do not continue to receive retry attempts indefinitely, maintaining system efficiency and preventing unnecessary load on both the sender and receiver.
You can re-enable a disabled webhook by clicking on the **Enable webhook** button in the webhook details page.
# Verify webhook requests
Source: https://dub.co/docs/concepts/webhooks/verify-webhook-requests
Learn how to verify webhook requests to ensure they're coming from Dub.
With signature verification, you can determine if the webhook came from Dub, and has not been tampered with in transit.
All webhooks are delivered with a `Dub-Signature` header. Dub generates this header using a secret key that only you and Dub know.
An example header looks like this:
```
Dub-Signature: c9ed6a2abf93f59d761eea69908d8de00f4437b5b6d7cd8b9bf5719cbe61bf46
```
## Finding your webhook's signing secret
You can find your webhook's signing secret in the **Update Details** tab:
Make sure to keep this secret safe by only storing it in a secure environment variable (e.g. `DUB_WEBHOOK_SECRET`). Do not commit it to git or add it in any client-side code.
## Verifying a webhook request
To verify, you can use the secret key to generate your own signature for each webhook. If both signatures match then you can be sure that a received event came from Dub.
The steps required are:
1. Get the raw body of the request.
2. Extract the signature from the `Dub-Signature` header.
3. Calculate the HMAC of the raw body using the `SHA-256` hash function and the secret.
4. Compare the calculated `HMAC` with the one sent in the `Dub-Signature` header. If they match, the webhook is verified.
Here's an example of how you can verify a webhook request in different languages:
```javascript Next.js
export const POST = async (req: Request) => {
const webhookSignature = req.headers.get("Dub-Signature");
if (!webhookSignature) {
return new Response("No signature provided.", { status: 401 });
}
// Copy this from the webhook details page
const secret = process.env.DUB_WEBHOOK_SECRET;
if (!secret) {
return new Response("No secret provided.", { status: 401 });
}
// Make sure to get the raw body from the request
const rawBody = await req.text();
const computedSignature = crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
if (webhookSignature !== computedSignature) {
return new Response("Invalid signature", { status: 400 });
}
// Handle the webhook event
// ...
};
```
```python Python
import hmac
import hashlib
def webhook():
# Get the signature from the header
webhook_signature = request.headers.get('Dub-Signature')
if not webhook_signature:
abort(401, 'No signature provided.')
# Copy this from the webhook details page
secret = os.environ.get('DUB_WEBHOOK_SECRET')
if not secret:
abort(401, 'No secret provided.')
# Get the raw body of the request
raw_body = request.data
# Calculate the HMAC
computed_signature = hmac.new(
secret.encode('utf-8'),
raw_body,
hashlib.sha256
).hexdigest()
if webhook_signature != computed_signature:
abort(400, 'Invalid signature')
# Handle the webhook event
# ...
return 'OK', 200
```
```go Go
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"io/ioutil"
"net/http"
"os"
)
func webhookHandler(w http.ResponseWriter, r *http.Request) {
// Get the signature from the header
webhookSignature := r.Header.Get("Dub-Signature")
if webhookSignature == "" {
http.Error(w, "No signature provided.", http.StatusUnauthorized)
return
}
// Copy this from the webhook details page
secret := os.Getenv("DUB_WEBHOOK_SECRET")
if secret == "" {
http.Error(w, "No secret provided.", http.StatusUnauthorized)
return
}
// Read the raw body
body, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, "Error reading request body", http.StatusInternalServerError)
return
}
// Calculate the HMAC
h := hmac.New(sha256.New, []byte(secret))
h.Write(body)
computedSignature := hex.EncodeToString(h.Sum(nil))
if webhookSignature != computedSignature {
http.Error(w, "Invalid signature", http.StatusBadRequest)
return
}
// Handle the webhook event
// ...
w.WriteHeader(http.StatusOK)
w.Write([]byte("OK"))
}
```
## Why is signature verification important?
Signature verification is a crucial security measure that protects against request forgery and data tampering. Without verification, malicious actors could send fake webhook events to your endpoint, potentially triggering unauthorized actions.
The HMAC-SHA256 signature verification process ensures that only Dub can generate valid webhook requests and that payloads haven't been modified in transit. This provides both authentication (confirming the sender is Dub) and integrity (ensuring the message hasn't been tampered with).
# Appwrite
Source: https://dub.co/docs/conversions/leads/appwrite
Learn how to track lead conversion events with Appwrite and Dub
Conversion tracking require a [Business plan](https://dub.co/pricing)
subscription or higher.
When it comes to [conversion tracking](/conversions/quickstart), a `lead` event happens when a user performs an action that indicates interest in your product or service. This could be anything from:
* Signing up for an account
* Adding a product to cart
* Joining a mailing list
In this guide, we will be focusing on tracking new user sign-ups for a SaaS application that uses Appwrite for user authentication.
## Prerequisites
Before you get started, make sure you follow the [Dub Conversions quickstart guide](/conversions/quickstart) to get Dub Conversions set up for your links:
1. [Enable conversion tracking for your links](/conversions/quickstart#step-1-enable-conversion-tracking-for-your-links)
2. [Install the @dub/analytics client-side SDK](/sdks/client-side/introduction)
3. [Install the Dub server-side SDK](/sdks/overview#server-side-sdks)
## Configure Appwrite
Next, configure Appwrite to track lead conversion events during the sign up process.
Head to [Appwrite Cloud](https://apwr.dev/appwrite-dub) and create a new project.
Create a new API key with the `sessions.write` scope enabled and save the API key for later use. You can also copy your project ID and endpoint from the project's Settings page.
Then, in your Next.js app, install the Appwrite Node.js SDK.
```bash
npm i node-appwrite
```
Add the following environment variables to your app.
```bash
NEXT_PUBLIC_APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1
NEXT_PUBLIC_APPWRITE_PROJECT=
NEXT_APPWRITE_KEY=
NEXT_DUB_API_KEY=
```
Add the `DubAnalytics` component from the `@dub/analytics` package to your app’s root layout.
```tsx src/app/layout.tsx
import type { Metadata } from 'next';
import { Analytics as DubAnalytics } from '@dub/analytics/react';
export const metadata: Metadata = {
title: 'Appwrite Dub Leads Example',
description: 'Appwrite Dub Leads Tracking example app with Next.js'
};
export default function RootLayout({
children
}: Readonly<{
children: React.ReactNode;
}>) {
return (
{children}
);
}
```
Create the Appwrite Session and Admin client (necessary for SSR apps, as explained in the [Appwrite docs](https://appwrite.io/docs/products/auth/server-side-rendering)). Additionally, create a function to verify user login.
```ts src/lib/server/appwrite.ts
'use server';
import { Client, Account } from 'node-appwrite';
import { cookies } from 'next/headers';
export async function createSessionClient() {
const client = new Client()
.setEndpoint(process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT as string)
.setProject(process.env.NEXT_PUBLIC_APPWRITE_PROJECT as string);
const session = (await cookies()).get('my-custom-session');
if (!session || !session.value) {
throw new Error('No session');
}
client.setSession(session.value);
return {
get account() {
return new Account(client);
}
};
}
export async function createAdminClient() {
const client = new Client()
.setEndpoint(process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT as string)
.setProject(process.env.NEXT_PUBLIC_APPWRITE_PROJECT as string)
.setKey(process.env.NEXT_APPWRITE_KEY as string);
return {
get account() {
return new Account(client);
}
};
}
```
Create the Dub client and send leads to Dub using the `dub.track.lead()` function.
```ts src/lib/server/dub.ts
import type { Models } from 'node-appwrite';
import { Dub } from 'dub';
const dub = new Dub({
token: process.env.NEXT_DUB_API_KEY
});
export function addDubLead(user: Models.User, dub_id: string) {
dub.track.lead({
clickId: dub_id,
eventName: 'Sign Up',
externalId: user.$id,
customerName: user.name,
customerEmail: user.email
});
}
```
In the `/auth` page, use the Appwrite Admin client to allow users to sign up. Post sign up, check if the `dub_id` cookie is present, send a lead event to Dub if found, and delete the `dub_id` cookie.
```tsx src/app/auth/page.tsx
import { ID } from 'node-appwrite';
import { createAdminClient, getLoggedInUser } from '@/lib/server/appwrite';
import { cookies } from 'next/headers';
import { redirect } from 'next/navigation';
import { addDubLead } from '@/lib/server/dub';
async function signUpWithEmail(formData: any) {
'use server';
// Get sign up info from form
const email = formData.get('email');
const password = formData.get('password');
const name = formData.get('name');
// Create account and session using Appwrite
const { account } = await createAdminClient();
const user = await account.create(ID.unique(), email, password, name);
const session = await account.createEmailPasswordSession(email, password);
(await cookies()).set('my-custom-session', session.secret, {
path: '/',
httpOnly: true,
sameSite: 'strict',
secure: true
});
// Check if Dub ID is present in cookies and track lead if found
const dub_id = (await cookies()).get('dub_id')?.value;
if (dub_id) {
addDubLead(user, dub_id);
(await cookies()).delete('dub_id');
}
// Redirect to success page
redirect('/auth/success');
}
export default async function SignUpPage() {
// Verify active user session and redirect to success page if found
const user = await getLoggedInUser();
if (user) redirect('/auth/success');
return (
<>
>
);
}
```
Here's the full list of attributes you can pass when sending a lead event:
| Property | Required | Description |
| :--------------- | :------- | :----------------------------------------------------------------------------------------------------------------------- |
| `clickId` | **Yes** | The unique `dub_id` parameter that the lead conversion event is attributed to. |
| `eventName` | **Yes** | The name of the event. Example: "Sign up". |
| `externalId` | **Yes** | The unique ID of the customer in your system. Will be used to identify and attribute all future events to this customer. |
| `customerEmail` | No | The email address of the customer. If not passed, a random email address will be generated. |
| `customerName` | No | The name of the customer. If not passed, a random name will be generated (e.g. "Big Red Caribou"). |
| `customerAvatar` | No | The avatar URL of the customer. If not passed, a random avatar URL will be generated. |
## Example App
To learn more about how to track leads with Appwrite, check out the following example app:
See how to track new user sign-ups with Appwrite and the Dub SDK.
## View your conversions
Once you've enabled conversion tracking for your links, all your tracked conversions will show up on your [Analytics dashboard](https://app.dub.co/analytics). We provide 3 different views to help you understand your conversions:
* **Time-series**: A [time-series view](https://dub.co/help/article/dub-analytics#1-time-series-analytics-chart) of the number clicks, leads and sales.
* **Funnel chart**: A funnel chart view visualizing the conversion & dropoff rates across the different steps in the conversion funnel (clicks → leads → sales).
* **Real-time events stream**: A [real-time events stream](https://dub.co/help/article/real-time-events-stream) of every single conversion event that occurs across all your links in your workspace.
# Auth0
Source: https://dub.co/docs/conversions/leads/auth0
Learn how to track lead conversion events with Auth0 and Dub
Conversion tracking require a [Business plan](https://dub.co/pricing)
subscription or higher.
When it comes to [conversion tracking](/conversions/quickstart), a `lead` event happens when a user performs an action that indicates interest in your product or service. This could be anything from:
* Signing up for an account
* Adding a product to cart
* Joining a mailing list
In this guide, we will be focusing on tracking new user sign-ups for a SaaS application that uses Auth0 for user authentication.
## Prerequisites
Before you get started, make sure you follow the [Dub Conversions quickstart guide](/conversions/quickstart) to get Dub Conversions set up for your links:
1. [Enable conversion tracking for your links](/conversions/quickstart#step-1-enable-conversion-tracking-for-your-links)
2. [Install the @dub/analytics client-side SDK](/sdks/client-side/introduction)
3. [Install the Dub server-side SDK](/sdks/overview#server-side-sdks)
## Configure Auth0
Next, configure Auth0 to track lead conversion events.
Here's how it works in a nutshell:
1. In the sign in `afterCallback` function, check if the user is a new sign up.
2. If the user is a new sign up, check if the `dub_id` cookie is present.
3. If the `dub_id` cookie is present, send a lead event to Dub using `dub.track.lead`
4. Delete the `dub_id` cookie.
```typescript app/api/auth/[auth0]/route.js
import { handleAuth, handleCallback, type Session } from "@auth0/nextjs-auth0";
import { cookies } from "next/headers";
import { dub } from "@/lib/dub";
const afterCallback = async (req: Request, session: Session) => {
const userExists = await getUser(session.user.email);
if (!userExists) {
createUser(session.user);
// check if dub_id cookie is present
const clickId = cookies().get("dub_id")?.value;
if (clickId) {
// send lead event to Dub
await dub.track.lead({
clickId,
eventName: "Sign Up",
externalId: session.user.id,
customerName: session.user.name,
customerEmail: session.user.email,
customerAvatar: session.user.image,
});
// delete the dub_id cookie
cookies().set("dub_id", "", {
expires: new Date(0),
});
}
return session;
}
};
export default handleAuth({
callback: handleCallback({ afterCallback }),
});
```
Here's the full list of attributes you can pass when sending a lead event:
| Property | Required | Description |
| :--------------- | :------- | :----------------------------------------------------------------------------------------------------------------------- |
| `clickId` | **Yes** | The unique `dub_id` parameter that the lead conversion event is attributed to. |
| `eventName` | **Yes** | The name of the event. Example: "Sign up". |
| `externalId` | **Yes** | The unique ID of the customer in your system. Will be used to identify and attribute all future events to this customer. |
| `customerEmail` | No | The email address of the customer. If not passed, a random email address will be generated. |
| `customerName` | No | The name of the customer. If not passed, a random name will be generated (e.g. "Big Red Caribou"). |
| `customerAvatar` | No | The avatar URL of the customer. If not passed, a random avatar URL will be generated. |
## View your conversions
Once you've enabled conversion tracking for your links, all your tracked conversions will show up on your [Analytics dashboard](https://app.dub.co/analytics). We provide 3 different views to help you understand your conversions:
* **Time-series**: A [time-series view](https://dub.co/help/article/dub-analytics#1-time-series-analytics-chart) of the number clicks, leads and sales.
* **Funnel chart**: A funnel chart view visualizing the conversion & dropoff rates across the different steps in the conversion funnel (clicks → leads → sales).
* **Real-time events stream**: A [real-time events stream](https://dub.co/help/article/real-time-events-stream) of every single conversion event that occurs across all your links in your workspace.
# Better Auth
Source: https://dub.co/docs/conversions/leads/better-auth
Learn how to track lead conversion events with Better Auth and Dub
Conversion tracking require a [Business plan](https://dub.co/pricing)
subscription or higher.
When it comes to [conversion tracking](/conversions/quickstart), a `lead` event happens when a user performs an action that indicates interest in your product or service. This could be anything from:
* Signing up for an account
* Adding a product to cart
* Joining a mailing list
In this guide, we will be focusing on tracking new user sign-ups for a SaaS application that uses Better Auth for user authentication.
## Prerequisites
Before you get started, make sure you follow the [Dub Conversions quickstart guide](/conversions/quickstart) to get Dub Conversions set up for your links:
1. [Enable conversion tracking for your links](/conversions/quickstart#step-1-enable-conversion-tracking-for-your-links)
2. [Install the @dub/analytics client-side SDK](/sdks/client-side/introduction)
3. [Install the Dub server-side SDK](/sdks/overview#server-side-sdks)
## Installation
To get started, simply install the [`@dub/better-auth` plugin](https://www.npmjs.com/package/@dub/better-auth) via your preferred package manager:
```bash npm
npm install @dub/better-auth
```
```bash yarn
yarn add @dub/better-auth
```
```bash pnpm
pnpm add @dub/better-auth
```
```bash bun
bun add @dub/better-auth
```
Then, add the plugin to your better-auth config file:
```ts auth.ts
import { dubAnalytics } from "@dub/better-auth";
import { betterAuth } from "better-auth";
import { Dub } from "dub";
const dub = new Dub();
export const auth = betterAuth({
plugins: [
dubAnalytics({
dubClient: dub,
}),
],
});
```
## View your conversions
Once you've enabled conversion tracking for your links, all your tracked conversions will show up on your [Analytics dashboard](https://app.dub.co/analytics). We provide 3 different views to help you understand your conversions:
* **Time-series**: A [time-series view](https://dub.co/help/article/dub-analytics#1-time-series-analytics-chart) of the number clicks, leads and sales.
* **Funnel chart**: A funnel chart view visualizing the conversion & dropoff rates across the different steps in the conversion funnel (clicks → leads → sales).
* **Real-time events stream**: A [real-time events stream](https://dub.co/help/article/real-time-events-stream) of every single conversion event that occurs across all your links in your workspace.
# Clerk
Source: https://dub.co/docs/conversions/leads/clerk
Learn how to track lead conversion events with Clerk and Dub
Conversion tracking require a [Business plan](https://dub.co/pricing)
subscription or higher.
When it comes to [conversion tracking](/conversions/quickstart), a `lead` event happens when a user performs an action that indicates interest in your product or service. This could be anything from:
* Signing up for an account
* Adding a product to cart
* Joining a mailing list
In this guide, we will be focusing on tracking new user sign-ups for a SaaS application that uses Clerk for user authentication.
## Prerequisites
Before you get started, make sure you follow the [Dub Conversions quickstart guide](/conversions/quickstart) to get Dub Conversions set up for your links:
1. [Enable conversion tracking for your links](/conversions/quickstart#step-1-enable-conversion-tracking-for-your-links)
2. [Install the @dub/analytics client-side SDK](/sdks/client-side/introduction)
3. [Install the Dub server-side SDK](/sdks/overview#server-side-sdks)
## Configure Clerk
Next, configure Clerk to track lead conversion events when a new user signs up. Here's a quick video showing how to do this:
Here's a quick summary of the steps:
Add the following environment variables to your app:
```bash
# get it here: https://dashboard.clerk.com/apps/new
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=your_publishable_key
CLERK_SECRET_KEY=your_secret_key
# get it here: https://d.to/tokens
DUB_API_KEY=your_api_key
```
Add the following JSON as a [custom claim](https://clerk.com/docs/references/nextjs/add-onboarding-flow#add-custom-claims-to-your-session-token) to your Clerk session token:
```json Clerk Session Token
{
"metadata": "{{user.public_metadata}}"
}
```
Extend the [`@dub/analytics` package](/sdks/client-side/introduction) to include a `trackLead` server action.
```tsx components/dub-analytics.tsx
"use client";
import { trackLead } from "@/actions/track-lead";
import { useUser } from "@clerk/nextjs";
import { Analytics, AnalyticsProps } from "@dub/analytics/react";
import { useEffect } from "react";
export function DubAnalytics(props: AnalyticsProps) {
const { user } = useUser();
useEffect(() => {
if (!user || user.publicMetadata.dubClickId) return;
// if the user is loaded but hasn't been persisted to Dub yet, track the lead event
trackLead({
id: user.id,
name: user.fullName!,
email: user.primaryEmailAddress?.emailAddress,
avatar: user.imageUrl,
}).then(async (res) => {
if (res.ok) await user.reload();
else console.error(res.error);
});
// you can also use an API route instead of a server action
/*
fetch("/api/track-lead", {
method: "POST",
body: JSON.stringify({
id: user.id,
name: user.fullName,
email: user.primaryEmailAddress?.emailAddress,
avatar: user.imageUrl,
}),
}).then(res => {
if (res.ok) await user.reload();
else console.error(res.statusText);
});
*/
}, [user]);
return ;
}
```
Then, add the `DubAnalytics` component to your app's root layout component:
```tsx app/layout.tsx
import { DubAnalytics } from "@/components/dub-analytics";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
{children}
);
}
```
On the server side, implement the `trackLead` server action. Alternatively, you can also create an API route instead:
```tsx /actions/track-lead.ts
// This is a server action
"use server";
import { dub } from "@/lib/dub";
import { clerkClient } from "@clerk/nextjs/server";
import { cookies } from "next/headers";
export async function trackLead({
id,
name,
email,
avatar,
}: {
id: string;
name?: string | null;
email?: string | null;
avatar?: string | null;
}) {
try {
const cookieStore = await cookies();
const dubId = cookieStore.get("dub_id")?.value;
if (dubId) {
// Send lead event to Dub
await dub.track.lead({
clickId: dubId,
eventName: "Sign Up",
externalId: id,
customerName: name,
customerEmail: email,
customerAvatar: avatar,
});
// Delete the dub_id cookie
cookieStore.set("dub_id", "", {
expires: new Date(0),
});
}
const clerk = await clerkClient();
await clerk.users.updateUser(id, {
publicMetadata: {
dubClickId: dubId || "n/a",
},
});
return { ok: true };
} catch (error) {
console.error("Error in trackLead:", error);
return { ok: false, error: (error as Error).message };
}
}
```
```tsx /api/track-lead/route.ts
// This is an API route
import { NextRequest, NextResponse } from "next/server";
export async function POST(req: NextRequest) {
// read dub_id from the request cookies
const dubId = req.cookies.get("dub_id")?.value;
if (dubId) {
// Send lead event to Dub
await dub.track.lead({
clickId: dubId,
eventName: "Sign Up",
externalId: id,
customerName: name,
customerEmail: email,
customerAvatar: avatar,
});
}
const clerk = await clerkClient();
await clerk.users.updateUser(id, {
publicMetadata: {
dubClickId: dubId || "n/a",
},
});
const res = NextResponse.json({ ok: true });
// Delete the dub_id cookie
res.cookies.set("dub_id", "", {
expires: new Date(0),
});
return res;
}
```
Here's the full list of attributes you can pass when sending a lead event:
| Property | Required | Description |
| :--------------- | :------- | :----------------------------------------------------------------------------------------------------------------------- |
| `clickId` | **Yes** | The unique `dub_id` parameter that the lead conversion event is attributed to. |
| `eventName` | **Yes** | The name of the event. Example: "Sign up". |
| `externalId` | **Yes** | The unique ID of the customer in your system. Will be used to identify and attribute all future events to this customer. |
| `customerEmail` | No | The email address of the customer. If not passed, a random email address will be generated. |
| `customerName` | No | The name of the customer. If not passed, a random name will be generated (e.g. "Big Red Caribou"). |
| `customerAvatar` | No | The avatar URL of the customer. If not passed, a random avatar URL will be generated. |
## Example App
To learn more about how to track leads with Clerk, check out the following example app:
See how to track new user sign-ups with Clerk and the Dub SDK.
## View your conversions
Once you've enabled conversion tracking for your links, all your tracked conversions will show up on your [Analytics dashboard](https://app.dub.co/analytics). We provide 3 different views to help you understand your conversions:
* **Time-series**: A [time-series view](https://dub.co/help/article/dub-analytics#1-time-series-analytics-chart) of the number clicks, leads and sales.
* **Funnel chart**: A funnel chart view visualizing the conversion & dropoff rates across the different steps in the conversion funnel (clicks → leads → sales).
* **Real-time events stream**: A [real-time events stream](https://dub.co/help/article/real-time-events-stream) of every single conversion event that occurs across all your links in your workspace.
# Introduction
Source: https://dub.co/docs/conversions/leads/introduction
Learn how to track lead conversion events with Dub Conversions
Conversion tracking require a [Business plan](https://dub.co/pricing)
subscription or higher.
When it comes to [conversion tracking](/conversions/quickstart), a `lead` event happens when a user performs an action that indicates interest in your product or service. This could be anything from:
* Signing up for an account
* Adding a product to cart
* Joining a mailing list
In this guide, we will be focusing on tracking new user sign-ups for a SaaS application as lead events on Dub.
## Prerequisites
Before you get started, make sure you follow the [Dub Conversions quickstart guide](/conversions/quickstart) to get Dub Conversions set up for your links:
1. [Enable conversion tracking for your links](/conversions/quickstart#step-1-enable-conversion-tracking-for-your-links)
2. [Install the @dub/analytics client-side SDK](/sdks/client-side/introduction)
3. [Install the Dub server-side SDK](/sdks/overview#server-side-sdks)
## Configure lead tracking
Depending on which authentication framework you're using, we have a few pre-built guides to help you track lead events when a new user signs up for your SaaS application:
If you're using the [Remix framework](https://remix.run/), you might run into an [issue](https://github.com/remix-run/remix/discussions/7922) where you get an empty/null value for the `dub_id` cookie. In that case, try using a library like [`cookie`](https://github.com/jshttp/cookie) instead:
```typescript
import cookie from "cookie";
const cookies = cookie.parse(request.headers.get("cookie") ?? "");
const dubId = cookies.dub_id;
```
If you're not using any of the frameworks listed above, you can use the following steps to track lead events:
1. Within the sign-up API request, retrieve the `dub_id` cookie that was created by the [`@dub/analytics` client-SDK](/sdks/client-side/introduction).
2. Send an event to the Dub API with the `dub_id` value, as well as your customer's ID and any other relevant information (email, name, avatar, etc.)
If you don't have a customer ID, you can also use the customer's `email`
instead.
## Code examples
Here are some examples of how you can track lead events using our [native SDKs](/sdks/overview):
```javascript Node.js
import { Dub } from "dub";
const dub = new Dub();
const dubId = req.cookies["dub_id"];
if (dubId) {
await dub.track.lead({
clickId: dubId,
eventName: "Sign Up",
externalId: customer.id,
customerName: customer.name,
customerEmail: customer.email,
customerAvatar: customer.avatar,
});
// delete the dub_id cookie
res.cookies.set("dub_id", "", {
expires: new Date(0),
});
}
```
```python Python
from dub import Dub
import os
dub = Dub(token=os.environ['DUB_API_KEY'])
dub_id = request.cookies.get('dub_id')
if dub_id:
dub.track.lead({
'click_id': dub_id,
'event_name': 'Sign Up',
'external_id': customer.id,
'customer_name': customer.name,
'customer_email': customer.email,
'customer_avatar': customer.avatar
})
# delete the dub_id cookie
response.delete_cookie('dub_id')
```
```go Go
package main
import (
"context"
dub "github.com/dubinc/dub-go"
"net/http"
)
d := dub.New(
dub.WithSecurity(os.Getenv("DUB_API_KEY")),
)
dubId, err := r.Cookie("dub_id")
if err == nil {
_, err = d.Track.Lead(context.Background(), &operations.TrackLeadRequest{
ClickId: dubId.Value,
EventName: "Sign Up",
ExternalId: customer.ID,
CustomerName: customer.Name,
CustomerEmail: customer.Email,
CustomerAvatar: customer.Avatar,
})
// delete the dub_id cookie
http.SetCookie(w, &http.Cookie{
Name: "dub_id",
Value: "",
Expires: time.Unix(0, 0),
})
}
```
```ruby Ruby
require 'dub'
dub = ::OpenApiSDK::Dub.new
dub.config_security(
::OpenApiSDK::Shared::Security.new(
token: ENV['DUB_API_KEY']
)
)
dub_id = cookies[:dub_id]
if dub_id
req = ::OpenApiSDK::Operations::TrackLeadRequest.new(
click_id: dub_id,
event_name: 'Sign Up',
external_id: customer.id,
customer_name: customer.name,
customer_email: customer.email,
customer_avatar: customer.avatar
)
dub.track.lead(req)
# delete the dub_id cookie
cookies.delete(:dub_id)
end
```
```php PHP
setSecurity($_ENV["DUB_API_KEY"])->build();
$dubId = $_COOKIE['dub_id'] ?? null;
if ($dubId) {
$request = new Operations\TrackLeadRequest();
$request->clickId = $dubId;
$request->eventName = 'Sign Up';
$request->externalId = $customer->id;
$request->customerName = $customer->name;
$request->customerEmail = $customer->email;
$request->customerAvatar = $customer->avatar;
$dub->track->lead($request);
// delete the dub_id cookie
setcookie('dub_id', '', time() - 3600);
}
```
Here's the full list of attributes you can pass when sending a lead event:
| Property | Required | Description |
| :--------------- | :------- | :----------------------------------------------------------------------------------------------------------------------- |
| `clickId` | **Yes** | The unique `dub_id` parameter that the lead conversion event is attributed to. |
| `eventName` | **Yes** | The name of the event. Example: "Sign up". |
| `externalId` | **Yes** | The unique ID of the customer in your system. Will be used to identify and attribute all future events to this customer. |
| `customerEmail` | No | The email address of the customer. If not passed, a random email address will be generated. |
| `customerName` | No | The name of the customer. If not passed, a random name will be generated (e.g. "Big Red Caribou"). |
| `customerAvatar` | No | The avatar URL of the customer. If not passed, a random avatar URL will be generated. |
## View your conversions
Once you've enabled conversion tracking for your links, all your tracked conversions will show up on your [Analytics dashboard](https://app.dub.co/analytics). We provide 3 different views to help you understand your conversions:
* **Time-series**: A [time-series view](https://dub.co/help/article/dub-analytics#1-time-series-analytics-chart) of the number clicks, leads and sales.
* **Funnel chart**: A funnel chart view visualizing the conversion & dropoff rates across the different steps in the conversion funnel (clicks → leads → sales).
* **Real-time events stream**: A [real-time events stream](https://dub.co/help/article/real-time-events-stream) of every single conversion event that occurs across all your links in your workspace.
# NextAuth.js
Source: https://dub.co/docs/conversions/leads/next-auth
Learn how to track lead conversion events with NextAuth.js and Dub
Conversion tracking require a [Business plan](https://dub.co/pricing)
subscription or higher.
When it comes to [conversion tracking](/conversions/quickstart), a `lead` event happens when a user performs an action that indicates interest in your product or service. This could be anything from:
* Signing up for an account
* Adding a product to cart
* Joining a mailing list
In this guide, we will be focusing on tracking new user sign-ups for a SaaS application that uses NextAuth.js for user authentication.
## Prerequisites
Before you get started, make sure you follow the [Dub Conversions quickstart guide](/conversions/quickstart) to get Dub Conversions set up for your links:
1. [Enable conversion tracking for your links](/conversions/quickstart#step-1-enable-conversion-tracking-for-your-links)
2. [Install the @dub/analytics client-side SDK](/sdks/client-side/introduction)
3. [Install the Dub server-side SDK](/sdks/overview#server-side-sdks)
## Configure NextAuth.js Options
Then, set up your NextAuth.js configuration options to track lead conversion events using the `dub` TypeScript SDK.
Here's how it works in a nutshell:
1. Use NextAuth's [`signIn` event](https://next-auth.js.org/configuration/events#signin) to detect when there's a new sign up.
2. If the user is a new sign up, check if the `dub_id` cookie is present.
3. If the `dub_id` cookie is present, send a lead event to Dub using `dub.track.lead`
4. Delete the `dub_id` cookie.
Under the hood, Dub records the user as a customer and associates them with the click event that they came from. The user's unique ID is now the source of truth for all future events – hence why we don't need the `dub_id` cookie anymore.
```typescript App Router
// app/api/auth/[...nextauth]/options.ts
import type { NextAuthOptions } from "next-auth";
import { cookies } from "next/headers";
import { dub } from "@/lib/dub";
export const authOptions: NextAuthOptions = {
...otherAuthOptions, // your other NextAuth options
events: {
async signIn(message) {
// if it's a new sign up
if (message.isNewUser) {
const cookieStore = await cookies();
// check if dub_id cookie is present
const dub_id = cookieStore.get("dub_id")?.value;
if (dub_id) {
// send lead event to Dub
await dub.track.lead({
clickId: dub_id,
eventName: "Sign Up",
externalId: user.id,
customerName: user.name,
customerEmail: user.email,
customerAvatar: user.image,
});
// delete the cookies
cookieStore.delete("dub_id");
cookieStore.delete("dub_partner_data");
}
}
},
},
};
```
```typescript Pages Router
// pages/api/auth/[...nextauth]/options.ts
import type { NextApiRequest } from "next";
import type { NextAuthOptions } from "next-auth";
import { dub } from "@/lib/dub";
export const getOptions = (req: NextApiRequest): NextAuthOptions => ({
...otherAuthOptions, // your other NextAuth options
events: {
async signIn(message) {
// if it's a new sign up
if (message.isNewUser) {
// check if dub_id cookie is present
const { dub_id } = req.cookies;
if (dub_id) {
// send lead event to Dub
await dub.track.lead({
clickId: dub_id,
eventName: "Sign Up",
externalId: user.id,
customerName: user.name,
customerEmail: user.email,
customerAvatar: user.image,
});
}
}
},
},
});
```
In NextAuth.js, the `isNewUser` flag will [only be available if you're using
`next-auth`'s database
implementation](https://next-auth.js.org/configuration/options#callbacks)
(otherwise it'll return `undefined`). In that case, you should move the logic
above to the [`signIn`
`callback`](https://next-auth.js.org/configuration/callbacks) instead.
Here's the full list of attributes you can pass when sending a lead event:
| Property | Required | Description |
| :--------------- | :------- | :----------------------------------------------------------------------------------------------------------------------- |
| `clickId` | **Yes** | The unique `dub_id` parameter that the lead conversion event is attributed to. |
| `eventName` | **Yes** | The name of the event. Example: "Sign up". |
| `externalId` | **Yes** | The unique ID of the customer in your system. Will be used to identify and attribute all future events to this customer. |
| `customerEmail` | No | The email address of the customer. If not passed, a random email address will be generated. |
| `customerName` | No | The name of the customer. If not passed, a random name will be generated (e.g. "Big Red Caribou"). |
| `customerAvatar` | No | The avatar URL of the customer. If not passed, a random avatar URL will be generated. |
## Create a NextAuth.js Route Handler
Finally, import the `authOptions` variable you created earlier and use `NextAuth` to create a handler for your NextAuth.js routes.
```typescript App Router
// app/api/auth/[...nextauth]/index.ts
import { authOptions } from "./options";
import NextAuth from "next-auth";
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };
```
```typescript Pages Router
// pages/api/auth/[...nextauth]/index.ts
import type { NextApiRequest, NextApiResponse } from "next";
import NextAuth from "next-auth";
import { getOptions } from "./options";
const handler = (req: NextApiRequest, res: NextApiResponse) =>
NextAuth(req, res, getOptions(req));
export default handler;
```
## View your conversions
Once you've enabled conversion tracking for your links, all your tracked conversions will show up on your [Analytics dashboard](https://app.dub.co/analytics). We provide 3 different views to help you understand your conversions:
* **Time-series**: A [time-series view](https://dub.co/help/article/dub-analytics#1-time-series-analytics-chart) of the number clicks, leads and sales.
* **Funnel chart**: A funnel chart view visualizing the conversion & dropoff rates across the different steps in the conversion funnel (clicks → leads → sales).
* **Real-time events stream**: A [real-time events stream](https://dub.co/help/article/real-time-events-stream) of every single conversion event that occurs across all your links in your workspace.
# Segment
Source: https://dub.co/docs/conversions/leads/segment
Learn how to track lead conversion events with Segment and Dub
Conversion tracking require a [Business plan](https://dub.co/pricing)
subscription or higher.
When it comes to [conversion tracking](/conversions/quickstart), a `lead` event happens when a user performs an action that indicates interest in your product or service. This could be anything from:
* Signing up for an account
* Adding a product to cart
* Joining a mailing list
In this guide, we will be focusing on tracking new user sign-ups for a SaaS application that uses Segment to collect customer data.
## Prerequisites
Before you get started, make sure you follow the [Dub Conversions quickstart guide](/conversions/quickstart) to get Dub Conversions set up for your links:
1. [Enable conversion tracking for your links](/conversions/quickstart#step-1-enable-conversion-tracking-for-your-links)
2. [Install the @dub/analytics client-side SDK](/sdks/client-side/introduction)
## Configure Segment Action
Next, configure [Segment Dub (Actions)](https://app.segment.com/goto-my-workspace/destinations/catalog/actions-dub) to track lead conversion events.
Head to [Segment Dub (Actions)](https://app.segment.com/goto-my-workspace/destinations/catalog/actions-dub) and add the destination to your Segment workspace.
In the Dub (Actions) destination settings, fill out the following fields:
* **Name:** Enter a name to help you identify this destination in Segment.
* **API Key:** Enter your Dub API key. You can find this in the [Dub Dashboard](https://app.dub.co/settings/tokens).
* **Enable Destination:** Toggle this on to allow Segment to send data to Dub.
Once completed, click **Save Changes**.
Next, you’ll choose the **Track a lead** action from the list of available actions.
By default, this action is configured to send lead data to Dub when the **Event Name** is **Sign Up**.
Below the selected action, you’ll see the mapping for that action.
You can customize the trigger and mapping to fit the specific needs of your application.
Finally, click **Next** and then **Save and enable** to add the mapping to the destination.
On the server side, you’ll use the `@segment/analytics-node` SDK to send lead events to Segment.
Make sure to include relevant user traits such as `name`, `email`, and `clickId` in the payload.
You’ll also need to ensure that the `clickId` field is properly mapped in your Segment Actions destination so that it’s forwarded correctly to Dub.
```tsx
import { Analytics } from "@segment/analytics-node";
const segment = new Analytics({
writeKey: "",
});
const cookieStore = await cookies();
const clickId = cookieStore.get("dub_id")?.value;
segment.track({
userId: id,
event: "Sign Up",
context: {
traits: {
name,
email,
avatar,
clickId,
},
},
integrations: {
All: true,
},
});
```
Once the event is tracked, Segment will forward the lead data to Dub based on the mappings you’ve configured.
Here's the full list of attributes you can pass when sending a lead event:
| Property | Required | Description |
| :--------------- | :------- | :----------------------------------------------------------------------------------------------------------------------- |
| `clickId` | **Yes** | The unique `dub_id` parameter that the lead conversion event is attributed to. |
| `eventName` | **Yes** | The name of the event. Example: "Sign up". |
| `externalId` | **Yes** | The unique ID of the customer in your system. Will be used to identify and attribute all future events to this customer. |
| `customerEmail` | No | The email address of the customer. If not passed, a random email address will be generated. |
| `customerName` | No | The name of the customer. If not passed, a random name will be generated (e.g. "Big Red Caribou"). |
| `customerAvatar` | No | The avatar URL of the customer. If not passed, a random avatar URL will be generated. |
## Example App
To learn more about how to track leads with Segment, check out the following example app:
Next.js app using Segment to track new user sign-ups.
## View your conversions
Once you've enabled conversion tracking for your links, all your tracked conversions will show up on your [Analytics dashboard](https://app.dub.co/analytics). We provide 3 different views to help you understand your conversions:
* **Time-series**: A [time-series view](https://dub.co/help/article/dub-analytics#1-time-series-analytics-chart) of the number clicks, leads and sales.
* **Funnel chart**: A funnel chart view visualizing the conversion & dropoff rates across the different steps in the conversion funnel (clicks → leads → sales).
* **Real-time events stream**: A [real-time events stream](https://dub.co/help/article/real-time-events-stream) of every single conversion event that occurs across all your links in your workspace.
# Supabase
Source: https://dub.co/docs/conversions/leads/supabase
Learn how to track lead conversion events with Supabase and Dub
Conversion tracking require a [Business plan](https://dub.co/pricing)
subscription or higher.
When it comes to [conversion tracking](/conversions/quickstart), a `lead` event happens when a user performs an action that indicates interest in your product or service. This could be anything from:
* Signing up for an account
* Adding a product to cart
* Joining a mailing list
In this guide, we will be focusing on tracking new user sign-ups for a SaaS application that uses Supabase for user authentication.
## Prerequisites
Before you get started, make sure you follow the [Dub Conversions quickstart guide](/conversions/quickstart) to get Dub Conversions set up for your links:
1. [Enable conversion tracking for your links](/conversions/quickstart#step-1-enable-conversion-tracking-for-your-links)
2. [Install the @dub/analytics client-side SDK](/sdks/client-side/introduction)
3. [Install the Dub server-side SDK](/sdks/overview#server-side-sdks)
## Configure Supabase
Next, configure Supabase to track lead conversion events in the auth callback function.
Here's how it works in a nutshell:
1. In the `/api/auth/callback` route, check if:
* the `dub_id` cookie is present.
* the user is a new sign up (created in the last 10 minutes).
2. If the `dub_id` cookie is present and the user is a new sign up, send a lead event to Dub using `dub.track.lead`
3. Delete the `dub_id` cookie.
```typescript Next.js App Router
// app/api/auth/callback/route.ts
import { cookies } from "next/headers";
import { NextResponse } from "next/server";
import { createClient } from "@/lib/supabase/server";
import { waitUntil } from "@vercel/functions";
import { dub } from "@/lib/dub";
export async function GET(request: Request) {
const { searchParams, origin } = new URL(request.url);
const code = searchParams.get("code");
// if "next" is in param, use it as the redirect URL
const next = searchParams.get("next") ?? "/";
if (code) {
const supabase = createClient(cookies());
const { data, error } = await supabase.auth.exchangeCodeForSession(code);
if (!error) {
const { user } = data;
const dub_id = cookies().get("dub_id")?.value;
// if the user is created in the last 10 minutes, consider them new
const isNewUser =
new Date(user.created_at) > new Date(Date.now() - 10 * 60 * 1000);
// if the user is new and has a dub_id cookie, track the lead
if (dub_id && isNewUser) {
waitUntil(
dub.track.lead({
clickId: dub_id,
eventName: "Sign Up",
externalId: user.id,
customerName: user.user_metadata.name,
customerEmail: user.email,
customerAvatar: user.user_metadata.avatar_url,
})
);
// delete the clickId cookie
cookies().delete("dub_id");
}
return NextResponse.redirect(`${origin}${next}`);
}
}
// return the user to an error page with instructions
return NextResponse.redirect(`${origin}/auth/auth-code-error`);
}
```
```typescript Next.js Pages Router
// pages/api/auth/callback.ts
import { NextApiRequest, NextApiResponse } from "next";
import { createClient } from "@supabase/supabase-js";
import { dub } from "@/lib/dub";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const { code, next = "/" } = req.query;
const origin = `${req.headers["x-forwarded-proto"]}://${req.headers.host}`;
if (typeof code === "string") {
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY!
);
const { data, error } = await supabase.auth.exchangeCodeForSession(code);
if (!error) {
const { user } = data;
const { dub_id } = req.cookies;
// if the user is created in the last 10 minutes, consider them new
const isNewUser =
new Date(user.created_at) > new Date(Date.now() - 10 * 60 * 1000);
// if the user is new and has a dub_id cookie, track the lead
if (dub_id && isNewUser) {
dub.track
.lead({
clickId: dub_id,
eventName: "Sign Up",
externalId: user.id,
customerName: user.user_metadata.name,
customerEmail: user.email,
customerAvatar: user.user_metadata.avatar_url,
})
.catch(console.error); // Handle any errors in tracking
// delete the clickId cookie
res.setHeader(
"Set-Cookie",
`dub_id=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT`
);
}
return res.redirect(`${origin}${next}`);
}
}
// return the user to an error page with instructions
return res.redirect(`${origin}/auth/auth-code-error`);
}
```
Here's the full list of attributes you can pass when sending a lead event:
| Property | Required | Description |
| :--------------- | :------- | :----------------------------------------------------------------------------------------------------------------------- |
| `clickId` | **Yes** | The unique `dub_id` parameter that the lead conversion event is attributed to. |
| `eventName` | **Yes** | The name of the event. Example: "Sign up". |
| `externalId` | **Yes** | The unique ID of the customer in your system. Will be used to identify and attribute all future events to this customer. |
| `customerEmail` | No | The email address of the customer. If not passed, a random email address will be generated. |
| `customerName` | No | The name of the customer. If not passed, a random name will be generated (e.g. "Big Red Caribou"). |
| `customerAvatar` | No | The avatar URL of the customer. If not passed, a random avatar URL will be generated. |
## Example App
To learn more about how to track leads with Supabase, check out the following example app:
Check out a real-world example of this in action – Extrapolate uses Supabase
Auth and Next.js App Router to track new user sign-ups.
## View your conversions
Once you've enabled conversion tracking for your links, all your tracked conversions will show up on your [Analytics dashboard](https://app.dub.co/analytics). We provide 3 different views to help you understand your conversions:
* **Time-series**: A [time-series view](https://dub.co/help/article/dub-analytics#1-time-series-analytics-chart) of the number clicks, leads and sales.
* **Funnel chart**: A funnel chart view visualizing the conversion & dropoff rates across the different steps in the conversion funnel (clicks → leads → sales).
* **Real-time events stream**: A [real-time events stream](https://dub.co/help/article/real-time-events-stream) of every single conversion event that occurs across all your links in your workspace.
# Quickstart
Source: https://dub.co/docs/conversions/quickstart
Learn how to track conversion analytics with Dub.
Conversion tracking require a [Business plan](https://dub.co/pricing)
subscription or higher.
[Dub Conversions](https://dub.co/help/article/dub-conversions) is a powerful tool that lets you turn any [short link you create on Dub](https://dub.co/help/article/how-to-create-link) into a full attribution engine. This allows you to understand how well your links are translating to actual users and revenue dollars.
In this guide, we'll walk you through the steps to get started with Dub Conversions:
1. [Enable conversion tracking for your links](#step-1-enable-conversion-tracking-for-your-links)
2. [Install the `@dub/analytics` client-side SDK](#step-2-install-the-dub-analytics-client-side-sdk)
3. [Install the Dub server-side SDK + track conversion events](#step-3-install-the-dub-server-side-sdk-track-conversion-events)
4. [View your conversions](#step-4-view-your-conversions)
## Step 1: Enable conversion tracking for your links
First, you'll need to enable conversion tracking for your Dub links to be able to start tracking conversions.
There are a few ways to do this:
To enable conversion tracking for all future links in a workspace, you can do the following:
To enable conversion tracking for all future links in a workspace, you can do the following:
1. Navigate to your [workspace's Analytics settings page](https://app.dub.co/settings/analytics).
2. Toggle the **Workspace-level Conversion Tracking** switch to enable conversion tracking for the workspace.
This option will enable conversion tracking in the [Dub Link Builder](https://dub.co/help/article/dub-link-builder) for all future links.
If you don't want to enable conversion tracking for all your links in a workspace, you can also opt to enable it on a link-level.
To enable conversion tracking for a specific link, open the [Dub Link Builder](https://dub.co/help/article/dub-link-builder) for a link and toggle the **Conversion Tracking** switch.
You can also use the `C` keyboard shortcut when inside the link builder to
quickly enable conversion tracking for a given link.
Alternatively, you can also enable conversion tracking programmatically via the [Dub API](/api-reference/introduction). All you need to do is pass `trackConversion: true` when creating or updating a link:
```javascript Node.js
const link = await dub.links.create({
url: "https://dub.co",
trackConversion: true,
});
```
```python Python
link = d.links.create(url="https://dub.co", track_conversion=True)
```
```go Go
link, err := d.Links.Create(ctx, &dub.CreateLinkRequest{
URL: "https://dub.co",
TrackConversion: true,
})
```
```ruby Ruby
s.links.create_many(
::OpenApiSDK::Operations::CreateLinkRequest.new(
url: "https://dub.co",
track_conversion: true,
)
)
```
## Step 2: Install the `@dub/analytics` client-side SDK
Next, you'll need to install the [@dub/analytics client-side SDK](/sdks/client-side/introduction).
This script detects the `dub_id` query parameter and storing it as a first-party cookie, which will be used to attribute subsequent conversion events to the original link.
You can install the `@dub/analytics` script in several different ways:
Add Dub Analytics to your React app
Add Dub Analytics to your website
}
href="https://dub.co/docs/sdks/client-side/installation-guides/framer"
>
Add Dub Analytics to your Framer site
Add Dub Analytics to your Shopify store
Add Dub Analytics to your WP site
Add Dub Analytics to your Webflow site
You can **verify the installation** with the following tests:
1. Open the browser console and type in `_dubAnalytics` – if the script is installed correctly, you should see the `_dubAnalytics` object in the console.
2. Add the `?dub_id=test` query parameter to your website URL and make sure that the `dub_id` cookie is being set in your browser.
If both of these checks pass, the script is installed correctly. Otherwise, please make sure:
* The analytics script was added to the `` section of the page
* If you're using a content delivery network (CDN), make sure to purge any cached content
## Step 3: Install the Dub server-side SDK + track conversion events
If you are using Shopify, you can skip this step since our Shopify app will
automatically track conversions for you. Read the [Shopify integration
guide](https://dub.co/docs/conversions/sales/shopify) for more information.
Dub uses server-side event tracking to track conversions, which is more reliable than client-side tracking. Depending on which framework you're using, you can use our [native SDKs](/sdks/overview):
TypeScript library for the Dub API
Go library for the Dub API
Python library for the Dub API
Ruby library for the Dub API
If you're using a framework that isn't listed, you can use the Dub REST API to track events on the server-side:
* [`POST /track/lead`](https://dub.co/docs/api-reference/endpoint/track-lead)
* [`POST /track/sale`](https://dub.co/docs/api-reference/endpoint/track-sale)
Once you install the SDKs, you can start tracking conversion events.
### Tracking lead events
The first event you'll want to track is a `lead` event. This happens when a user performs an action that indicates interest in your product or service. This could be anything from:
* Signing up for an account
* Adding a product to cart
* Joining a mailing list
Our most common lead event is `Sign Up`, which happens when a user signs up for an account. Depending on which authentication framework you're using, here are a few examples of how to send `Sign Up` lead events:
The lead event will serve as the source of truth for the customer's identity and which link they came from. This means that all subsequent actions performed by the customer (e.g. upgrading their plan, purchasing a product) will automatically be attributed to the original link.
To learn more about tracking lead events with Dub, refer to the following resources:
Read the full guide on tracking lead events with Dub
View the full list of attributes you can pass when sending a lead event
### Tracking sale events
The second event you'll want to send is a `sale` event. This happens when a user purchases your product or service. This could be anything from:
* Subscribing to a paid plan
* Usage expansion (upgrading from one plan to another)
* Purchasing a product
Depending on which payment processor you're using, we offer native integrations for the following:
Alternatively, you can also send sale events manually using [our SDKs](/sdks/overview) or the [`POST /track/sale` API endpoint](https://dub.co/docs/api-reference/endpoint/track-sale).
To learn more about tracking sale events with Dub, refer to the following resources:
Read the full guide on tracking sale events with Dub
View the full list of attributes you can pass when sending a sale event
## Step 4: View your conversions
Once you've enabled conversion tracking for your links, all your tracked conversions will show up on your [Analytics dashboard](https://app.dub.co/analytics). We provide 3 different views to help you understand your conversions:
* **Time-series**: A [time-series view](https://dub.co/help/article/dub-analytics#1-time-series-analytics-chart) of the number clicks, leads and sales.
* **Funnel chart**: A funnel chart view visualizing the conversion & dropoff rates across the different steps in the conversion funnel (clicks → leads → sales).
* **Real-time events stream**: A [real-time events stream](https://dub.co/help/article/real-time-events-stream) of every single conversion event that occurs across all your links in your workspace.
## Example Apps
See the full example on GitHub.
# Introduction
Source: https://dub.co/docs/conversions/sales/introduction
Learn how to track sales conversion events with Dub Conversions
Conversion tracking require a [Business plan](https://dub.co/pricing)
subscription or higher.
When it comes to [conversion tracking](/conversions/quickstart), a `sale` event happens when a user purchases your product or service. Examples include:
* Subscribing to a paid plan
* Usage expansion (upgrading from one plan to another)
* Purchasing a product from your online store
In this guide, we will be focusing on tracking sales conversion events with Dub.
## Prerequisites
Before you get started, make sure you follow the [Dub Conversions quickstart guide](/conversions/quickstart) to get Dub Conversions set up for your links:
1. [Enable conversion tracking for your links](/conversions/quickstart#step-1-enable-conversion-tracking-for-your-links)
2. [Install the @dub/analytics client-side SDK](/sdks/client-side/introduction)
3. [Install the Dub server-side SDK](/sdks/overview#server-side-sdks)
Then, depending on your authentication provider, follow the relevant guide to set up lead conversion tracking:
## Step 1: Configure sale tracking
Next, depending on which payment processor you're using, you can leverage our native integrations to track sale events:
Tracking sale conversion events with Stripe and the Dub SDK
Tracking sale conversion events with Shopify and the Dub SDK
If you're not using any of the providers listed above, you can also manually track sale events using our [native SDKs](/sdks/overview) within your backend code:
```javascript Node.js
import { Dub } from "dub";
const dub = new Dub();
await dub.track.sale({
externalId: "cus_RBfbD57HDzPKpduI8elr5qHA",
amount: 100,
paymentProcessor: "stripe",
eventName: "E-book purchase",
invoiceId: "123456",
currency: "usd",
});
```
```python Python
from dub import Dub
import os
dub = Dub(token=os.environ['DUB_API_KEY'])
dub.track.sale({
'external_id': 'cus_RBfbD57HDzPKpduI8elr5qHA',
'amount': 100,
'payment_processor': 'stripe',
'event_name': 'E-book purchase',
'invoice_id': '123456',
'currency': 'usd'
})
```
```go Go
package main
import (
"context"
dub "github.com/dubinc/dub-go"
)
d := dub.New(
dub.WithSecurity(os.Getenv("DUB_API_KEY")),
)
_, err := d.Track.Sale(context.Background(), &operations.TrackSaleRequest{
ExternalId: "cus_RBfbD57HDzPKpduI8elr5qHA",
Amount: 100,
PaymentProcessor: "stripe",
EventName: "E-book purchase",
InvoiceId: "123456",
Currency: "usd",
})
```
```ruby Ruby
require 'dub'
dub = ::OpenApiSDK::Dub.new
dub.config_security(
::OpenApiSDK::Shared::Security.new(
token: ENV['DUB_API_KEY']
)
)
req = ::OpenApiSDK::Operations::TrackSaleRequest.new(
external_id: 'cus_RBfbD57HDzPKpduI8elr5qHA',
amount: 100,
payment_processor: 'stripe',
event_name: 'E-book purchase',
invoice_id: '123456',
currency: 'usd'
)
dub.track.sale(req)
```
```php PHP
setSecurity($_ENV["DUB_API_KEY"])->build();
$request = new Operations\TrackSaleRequest();
$request->externalId = 'cus_RBfbD57HDzPKpduI8elr5qHA';
$request->amount = 100;
$request->paymentProcessor = 'stripe';
$request->eventName = 'E-book purchase';
$request->invoiceId = '123456';
$request->currency = 'usd';
$dub->track->sale($request);
```
Here are the properties you can include when sending a sale event:
| Property | Required | Description |
| :----------------- | :------- | :------------------------------------------------------------------------------------------------------------------------------- |
| `externalId` | **Yes** | The unique ID of the customer in your system. Will be used to identify and attribute all future events to this customer. |
| `amount` | **Yes** | The amount of the sale in cents. |
| `paymentProcessor` | **Yes** | The payment processor that processed the sale. (E.g. [Stripe](/conversions/sales/stripe), [Shopify](/conversions/sales/shopify)) |
| `eventName` | No | The name of the event. Defaults to "Purchase". |
| `invoiceId` | No | The invoice ID of the sale. Can be used as a idempotency key – only one sale event can be recorded for a given invoice ID. |
| `currency` | No | The currency of the sale. Defaults to "usd". |
| `metadata` | No | An object containing additional information about the sale. |
## Step 2: View conversion results
And that's it – you're all set! You can now sit back, relax, and watch your conversion revenue grow. We provide 3 different views to help you understand your conversions:
* **Time-series**: A [time-series view](https://dub.co/help/article/dub-analytics#1-time-series-analytics-chart) of the number clicks, leads and sales.
* **Funnel chart**: A funnel chart view visualizing the conversion & dropoff rates across the different steps in the conversion funnel (clicks → leads → sales).
* **Real-time events stream**: A [real-time events stream](https://dub.co/help/article/real-time-events-stream) of every single conversion event that occurs across all your links in your workspace.
## Currency conversion support
For simplicity, Dub records all sales in the default currency of your Dub workspace. This means that if you pass a different currency, it will be automatically converted to USD for reporting consistency – using the latest foreign exchange rates.
```typescript TypScript
await dub.track.sale({
externalId: "cus_RBfbD57HDzPKpduI8elr5qHA",
amount: 15480, // this will be converted from PLN to USD
currency: "pln",
paymentProcessor: "stripe",
eventName: "Purchase",
});
```
The default currency for all Dub workspaces is currently set to `USD`. We will
add the ability to customize that in the future.
# Segment
Source: https://dub.co/docs/conversions/sales/segment
Learn how to track sales conversion events with Segment and Dub
Conversion tracking require a [Business plan](https://dub.co/pricing)
subscription or higher.
When it comes to [conversion tracking](/conversions/quickstart), a `sale` event happens when a user purchases your product or service.
In this guide, we will be focusing on tracking sales conversion events for a SaaS application that uses Segment to collect customer data.
## Prerequisites
Before you get started, make sure you follow the [Dub Conversions quickstart guide](/conversions/quickstart) to get Dub Conversions set up for your links:
1. [Enable conversion tracking for your links](/conversions/quickstart#step-1-enable-conversion-tracking-for-your-links)
2. [Install the @dub/analytics client-side SDK](/sdks/client-side/introduction)
If you’ve already set up the Dub (Actions) destination, you can skip the first two steps and jump straight to the Add Mapping section.
## Configure Segment Action
Next, configure [Segment Dub (Actions)](https://app.segment.com/goto-my-workspace/destinations/catalog/actions-dub) to track sales conversion events.
Head to [Segment Dub (Actions)](https://app.segment.com/goto-my-workspace/destinations/catalog/actions-dub) and add the destination to your Segment workspace.
In the Dub (Actions) destination settings, fill out the following fields:
* **Name:** Enter a name to help you identify this destination in Segment.
* **API Key:** Enter your Dub API key. You can find this in the [Dub Dashboard](https://app.dub.co/settings/tokens).
* **Enable Destination:** Toggle this on to allow Segment to send data to Dub.
Once completed, click **Save Changes**.
Next, you’ll choose the **Track a sale** action from the list of available actions.
By default, this action is configured to send sale data to Dub when the **Event Name** is **Order Completed**.
Below the selected action, you’ll see the mapping for that action.
You can customize the trigger and mapping to fit the specific needs of your application.
Finally, click **Next** and then **Save and enable** to add the mapping to the destination.
On the server side, you’ll use the `@segment/analytics-node` SDK to send sale events to Segment.
Make sure to include relevant properties such as `userId`, `payment_processor`, `order_id`, `currency`, and `revenue` in the payload.
```tsx
import { Analytics } from "@segment/analytics-node";
const segment = new Analytics({
writeKey: "",
});
segment.track({
userId: id,
event: "Order Completed",
properties: {
payment_processor: "stripe",
order_id: "ORD_123",
currency: "USD",
revenue: 1000,
},
integrations: {
All: true,
},
});
```
Once the event is tracked, Segment will forward the sale data to Dub based on the mappings you’ve configured.
Here are the properties you can include when sending a sale event:
| Property | Required | Description |
| :----------------- | :------- | :------------------------------------------------------------------------------------------------------------------------------- |
| `externalId` | **Yes** | The unique ID of the customer in your system. Will be used to identify and attribute all future events to this customer. |
| `amount` | **Yes** | The amount of the sale in cents. |
| `paymentProcessor` | **Yes** | The payment processor that processed the sale. (E.g. [Stripe](/conversions/sales/stripe), [Shopify](/conversions/sales/shopify)) |
| `eventName` | No | The name of the event. Defaults to "Purchase". |
| `invoiceId` | No | The invoice ID of the sale. Can be used as a idempotency key – only one sale event can be recorded for a given invoice ID. |
| `currency` | No | The currency of the sale. Defaults to "usd". |
| `metadata` | No | An object containing additional information about the sale. |
## Example App
To learn more about how to track sales with Segment, check out the following example app:
Next.js app using Segment to track sales.
* **Time-series**: A [time-series view](https://dub.co/help/article/dub-analytics#1-time-series-analytics-chart) of the number clicks, leads and sales.
* **Funnel chart**: A funnel chart view visualizing the conversion & dropoff rates across the different steps in the conversion funnel (clicks → leads → sales).
* **Real-time events stream**: A [real-time events stream](https://dub.co/help/article/real-time-events-stream) of every single conversion event that occurs across all your links in your workspace.
# Shopify
Source: https://dub.co/docs/conversions/sales/shopify
Learn how to track sale conversion events with Shopify and Dub
Conversion tracking require a [Business plan](https://dub.co/pricing)
subscription or higher.
When it comes to [conversion tracking](/conversions/quickstart), a `sale` event happens when a user purchases a product from your Shopify store.
In this guide, we will be focusing on tracking sale events from Shopify by leveraging Dub's Shopify integration.
## Step 1: Enable conversion tracking for your links
First, you'll need to enable conversion tracking for your Dub links to be able to start tracking conversions.
There are a few ways to do this:
1. [On a workspace-level](/conversions/quickstart#option-1-on-a-workspace-level)
2. [On a link-level](/conversions/quickstart#option-2-on-a-link-level)
3. [Via the API](/conversions/quickstart#option-3-via-the-api)
## Step 2: Install the Dub Shopify app
Install the [Dub Shopify App](https://d.to/shopify/app) from the App Store.
After installation, you will be prompted to link one of your Dub workspaces to
the app. Select **Connect** to establish a connection between your
Shopify store and your Dub workspace.
You'll be redirected back to your Shopify store after this step and you'll see a list of the links in your Dub workspace:
With the Shopify app, you can also create [conversion-enabled links](/conversions//quickstart#step-1-enable-conversion-tracking-for-your-links) directly from your Shopify store:
If you want a more powerful link builder, you can also use the [Dub Link Builder](https://dub.co/help/article/dub-link-builder) to create conversion-enabled links.
After installing the Dub Shopify app, the Dub Analytics script is added as an app embed. However, it needs to be activated manually to ensure it is included in your current theme.
To activate the Dub Analytics script, follow these steps:
1. Navigate to your Shopify admin panel.
2. Go to **Online Store** > **Themes**.
3. Click on **Customize** for your current theme.
4. In the theme editor, select the **App embeds** tab.
5. Locate the **Analytics Script** for the Dub Shopify app and toggle it to activate.
Dub’s Shopify integration will automatically forward the following events to Dub:
* `orders/paid`: This event is triggered when a customer completes a purchase on your Shopify store. It is utilized to track sales that originate from Dub links.
* `app/uninstalled`: This event occurs when the app is uninstalled from a store. It is used to remove the integration from your Dub workspace.
In addition to the above, we also subscribe to the mandatory compliance webhook topics that are required by Shopify.
## Step 3: View conversion results
And that's it – you're all set! You can now sit back, relax, and watch your conversion revenue grow. We provide 3 different views to help you understand your conversions:
* **Time-series**: A [time-series view](https://dub.co/help/article/dub-analytics#1-time-series-analytics-chart) of the number clicks, leads and sales.
* **Funnel chart**: A funnel chart view visualizing the conversion & dropoff rates across the different steps in the conversion funnel (clicks → leads → sales).
* **Real-time events stream**: A [real-time events stream](https://dub.co/help/article/real-time-events-stream) of every single conversion event that occurs across all your links in your workspace.
## Currency conversion support
For simplicity, Dub records all sales in the native currency of the Shopify store. For example, if you're using USD for your Shopify store, Dub will record all sales in USD – even if your customers are paying in a different currency.
```json orders/paid
// Shopify orders/paid event payload
// @see: https://shopify.dev/docs/api/webhooks?reference=toml#list-of-topics-orders/paid
{
...
"current_subtotal_price_set": {
"shop_money": {
"amount": "398.00", // this is the amount that Dub will record
"currency_code": "USD" // this is the currency of your Shopify store
},
"presentment_money": {
"amount": "572.25",
"currency_code": "CAD"
}
},
...
}
```
# Stripe
Source: https://dub.co/docs/conversions/sales/stripe
Learn how to track sale conversion events with Stripe and Dub
Conversion tracking require a [Business plan](https://dub.co/pricing)
subscription or higher.
When it comes to [conversion tracking](/conversions/quickstart), a `sale` event happens when a user purchases your product or service. Examples include:
* Subscribing to a paid plan
* Usage expansion (upgrading from one plan to another)
* Purchasing a product via [Stripe Payment Links](#option-1-using-stripe-payment-links)
In this guide, we will be focusing on tracking sale events with Stripe as the payment processor by leveraging Dub's Stripe integration.
## Installing the Dub Stripe integration
Dub comes with a powerful Stripe integration that automatically listens to payment events on Stripe and track them as sales on Dub.
Here's how you can install the Dub Stripe integration:
Navigate to the [Dub Stripe Integration](https://d.to/stripe/app) on the Stripe App Marketplace.
On the top right, click on **Install app** to install the Dub Conversions app on your Stripe account.
Alternatively, you can also install the Stripe app in test mode first to test
your end-to-end flow without involving real money.
Once the app is installed, click on **Continue to app settings** to finish the installation.
In the app settings page, click on **Connect workspace** to connect your Stripe account with your Dub workspace.
This will redirect you to the [Dub OAuth flow](/integrations/quickstart), where you can select the Dub workspace you want to connect to your Stripe account.
Once you click on **Authorize**, you will be redirected back to the Dub app settings page on Stripe, where you should see that the integration is now installed.
Once the integration is installed, Dub will automatically listen to the following events on Stripe and track them as sales on Dub:
* `customer.created`: When a new customer is created
* `customer.updated`: When a customer is updated
* `checkout.session.completed`: When a customer completes a checkout session
* `invoice.paid`: When an invoice is paid (for tracking recurring subscriptions)
* `charge.refunded`: When a charge is refunded (for tracking refunds and updating payout commissions for [Dub Partners](https://dub.partners))
## Tracking sales with the Dub Stripe integration
Depending on your setup, there are a few ways you can track sales with the Dub Stripe integration.
* [Option 1: Using Stripe Payment Links](#option-1%3A-using-stripe-payment-links)
* [Option 2: Using Stripe Checkout (recommended)](#option-2%3A-using-stripe-checkout-recommended)
* [Option 3: Using Stripe Customers](#option-3%3A-using-stripe-customers)
### Option 1: Using Stripe Payment Links
For this option to work, you need to [install the Dub Stripe
integration](#installing-the-dub-stripe-integration) and [enable conversion
tracking for your
links](/conversions/quickstart#step-1-enable-conversion-tracking-for-your-links)
first.
If you're using [Stripe Payment Links](https://docs.stripe.com/payment-links), simply add a `?dub_client_reference_id=1` query parameter to your Stripe Payment Link when shortening it on Dub.
Then, when a user clicks on the shortened link, Dub will automatically append the unique click ID as the `client_reference_id` [query parameter](https://docs.stripe.com/payment-links/url-parameters) to the payment link.
Finally, when the user completes the checkout flow, Dub will automatically [track the sale event](/api-reference/endpoint/track-sale) and [update the customer's `externalId`](/api-reference/endpoint/update-a-customer) with their Stripe customer ID for future reference.
Alternatively, if you have a marketing site that you're redirecting your users to first, you can do this instead:
1. [Install the @dub/analytics client-side SDK](/sdks/client-side/introduction), which automatically detects the `dub_id` in the URL and stores it as a first-party cookie on your site.
2. Then, retrieve and append the `dub_id` value as the `client_reference_id` parameter to the payment links on your pricing page / CTA button (prefixed with `dub_id_`).
```
https://buy.stripe.com/xxxxxx?client_reference_id=dub_id_xxxxxxxxxxxxxx
```
If you're using [Stripe Pricing Tables](https://docs.stripe.com/payments/checkout/pricing-table) – you'd want to pass the Dub click ID as a [`client-reference-id` attribute](https://docs.stripe.com/payments/checkout/pricing-table#handle-fulfillment-with-the-stripe-api) instead:
```html HTML
We offer plans that help any business!
```
```jsx React
import * as React from "react";
function PricingPage() {
// Paste the stripe-pricing-table snippet in your React component
return (
);
}
export default PricingPage;
```
If you're using Stripe's [Checkout Sessions API](https://docs.stripe.com/api/checkout/sessions/object) for a recurring subscription service, you might want to check out our [Stripe Checkout option](#option-2%3A-using-stripe-checkout-recommended) instead.
If your setup doesn't involve a [lead event](/conversions/leads/introduction) and goes straight to the Stripe checkout flow (e.g. for one-time purchases), you can simply pass the Dub click ID (prefixed with `dub_id_`) as the [`client_reference_id` parameter](https://docs.stripe.com/api/checkout/sessions/object#checkout_session_object-client_reference_id) to enable conversion tracking with Dub.
```javascript Node.js
const session = await stripe.checkout.sessions.create({
success_url: "https://example.com/success",
line_items: [
{
price: "price_xxxxxxxxxxxxxxxx",
quantity: 2,
},
],
mode: "payment",
client_reference_id: "dub_id_xxxxxxxxxxxxxx",
});
```
```python Python
stripe.checkout.Session.create(
success_url="https://example.com/success",
line_items=[{"price": "price_xxxxxxxxxxxxxxxx", "quantity": 2}],
mode="payment",
client_reference_id="dub_id_xxxxxxxxxxxxxx",
)
```
```go Go
params := &stripe.CheckoutSessionParams{
SuccessURL: stripe.String("https://example.com/success"),
LineItems: []*stripe.CheckoutSessionLineItemParams{
&stripe.CheckoutSessionLineItemParams{
Price: stripe.String("price_xxxxxxxxxxxxxxxx"),
Quantity: stripe.Int64(2),
},
},
Mode: stripe.String(string(stripe.CheckoutSessionModePayment)),
ClientReferenceID: stripe.String("dub_id_xxxxxxxxxxxxxx"),
};
result, err := session.New(params);
```
```php PHP
$stripe->checkout->sessions->create([
'success_url' => 'https://example.com/success',
'line_items' => [
[
'price' => 'price_xxxxxxxxxxxxxxxx',
'quantity' => 2,
],
],
'mode' => 'payment',
'client_reference_id' => "dub_id_xxxxxxxxxxxxxx",
]);
```
```ruby Ruby
Stripe::Checkout::Session.create({
success_url: 'https://example.com/success',
line_items: [
{
price: 'price_xxxxxxxxxxxxxxxx',
quantity: 2,
},
],
mode: 'payment',
client_reference_id: "dub_id_xxxxxxxxxxxxxx",
})
```
### Option 2: Using Stripe Checkout (recommended)
If you have a custom checkout flow that uses Stripe's `checkout.sessions.create` API, you'd want to associate the [Stripe customer object](https://docs.stripe.com/api/customers/object) with the user's unique ID in your database (which we tracked in the [lead conversion tracking step](/conversions/leads/introduction)).
This will allow Dub to automatically listen for purchase events from Stripe and associate them with the original click event (and by extension, the link that the user came from).
Remember in the [lead conversion tracking guide](/conversions/leads/introduction), we passed the user's unique user ID along with the click event ID in the `dub.track.lead` call?
```javascript Node.js
await dub.track.lead({
clickId,
eventName: "Sign Up",
externalId: user.id, // the unique user ID of the customer in your database
customerName: user.name,
customerEmail: user.email,
customerAvatar: user.image,
});
```
Under the hood, Dub records the user as a customer and associates them with the click event that they came from.
Then, when the user makes a purchase, Dub will automatically associate the checkout session details (invoice amount, currency, etc.) with the customer – and by extension, the original click event.
First, you'll need to complete the following prerequisites:
1. [Install the Dub Stripe integration](#installing-the-dub-stripe-integration)
2. [Enable conversion tracking for your links](/conversions/quickstart#step-1-enable-conversion-tracking-for-your-links)
3. [Install the @dub/analytics client-side SDK](/sdks/client-side/introduction)
4. [Install the Dub server-side SDK](/sdks/overview#server-side-sdks)
Then, when you [create a checkout session](https://docs.stripe.com/api/checkout/sessions/create), pass your customer's unique user ID in your database as the `dubCustomerId` value in the `metadata` field.
```javascript Node.js
import { stripe } from "@/lib/stripe";
const user = {
id: "user_123",
email: "user@example.com",
teamId: "team_xxxxxxxxx",
};
const priceId = "price_xxxxxxxxx";
const stripeSession = await stripe.checkout.sessions.create({
customer_email: user.email,
success_url: "https://app.domain.com/success",
line_items: [{ price: priceId, quantity: 1 }],
mode: "subscription",
client_reference_id: user.teamId,
metadata: {
dubCustomerId: user.id, // the unique user ID of the customer in your database
},
});
```
This way, when the customer completes their checkout session, Dub will automatically associate the checkout session details (invoice amount, currency, etc.) with the customer – and by extension, the original click event.
### Option 3: Using Stripe Customers
Alternatively, if you don't use Stripe's [checkout session creation flow](#option-2%3A-using-stripe-checkout-recommended), you can also pass the user ID and the click event ID (`dub_id`) in the [Stripe customer creation flow](https://docs.stripe.com/api/customers/create).
First, you'll need to complete the following prerequisites:
1. [Install the Dub Stripe integration](#installing-the-dub-stripe-integration)
2. [Enable conversion tracking for your links](/conversions/quickstart#step-1-enable-conversion-tracking-for-your-links)
3. [Install the @dub/analytics client-side SDK](/sdks/client-side/introduction)
Then, when you [create a Stripe customer](https://docs.stripe.com/api/customers/create), pass the user's unique user ID in your database as the `dubCustomerId` value in the `metadata` field.
```javascript Node.js
import { stripe } from "@/lib/stripe";
const user = {
id: "user_123",
email: "user@example.com",
teamId: "team_xxxxxxxxx",
};
const dub_id = req.headers.get("dub_id");
await stripe.customers.create({
email: user.email,
name: user.name,
metadata: {
dubCustomerId: user.id,
dubClickId: dub_id,
},
});
```
Alternatively, you can also pass the `dubCustomerId` and `dubClickId` values in the `metadata` field of the [Stripe customer update flow](https://docs.stripe.com/api/customers/update):
```javascript Node.js
import { stripe } from "@/lib/stripe";
const user = {
id: "user_123",
email: "user@example.com",
teamId: "team_xxxxxxxxx",
};
const dub_id = req.headers.get("dub_id");
await stripe.customers.update(user.id, {
metadata: {
dubCustomerId: user.id,
dubClickId: dub_id,
},
});
```
This way, when the customer makes a purchase, Dub will automatically associate the purchase details (invoice amount, currency, etc.) with the original click event.
## Currency conversion support
If you're using [Stripe's Adaptive Pricing](https://docs.stripe.com/payments/checkout/adaptive-pricing) feature, Dub will record the sale amount using the currency of your Stripe account:
```json checkout.session.completed
// Stripe checkout.session.completed event payload
{
"id": "{{EVENT_ID}}",
"object": "event",
"type": "checkout.session.completed",
"data": {
"object": {
"id": "{{SESSION_ID}}",
"object": "checkout.session",
"currency": "cad",
"amount_subtotal": 2055,
"amount_total": 2055,
"currency_conversion": {
"amount_subtotal": 1500,
"amount_total": 1500, // this is the amount that Dub will record
"source_currency": "usd", // the currency of your Stripe account
"fx_rate": "1.37"
}
}
}
}
```
If you're not using Stripe Adaptive Pricing, Dub will record the sale amount in the default currency of your Dub workspace. This means that if you pass a different currency, it will be automatically converted to USD for reporting consistency – using the latest foreign exchange rates.
```json checkout.session.completed
// Stripe checkout.session.completed event payload
{
"id": "{{EVENT_ID}}",
"object": "event",
"type": "checkout.session.completed",
"data": {
"object": {
"id": "{{SESSION_ID}}",
"object": "checkout.session",
"currency": "cad",
"amount_subtotal": 2055,
"amount_total": 2055 // this will be converted from CAD to USD
}
}
}
```
The default currency for all Dub workspaces is currently set to `USD`. We will
add the ability to customize that in the future.
## View conversion results
And that's it – you're all set! You can now sit back, relax, and watch your conversion revenue grow. We provide 3 different views to help you understand your conversions:
* **Time-series**: A [time-series view](https://dub.co/help/article/dub-analytics#1-time-series-analytics-chart) of the number clicks, leads and sales.
* **Funnel chart**: A funnel chart view visualizing the conversion & dropoff rates across the different steps in the conversion funnel (clicks → leads → sales).
* **Real-time events stream**: A [real-time events stream](https://dub.co/help/article/real-time-events-stream) of every single conversion event that occurs across all your links in your workspace.
## Example Apps
See the full example on GitHub.
# Introduction
Source: https://dub.co/docs/integrations
Integrate Dub with your favorite tools and services.
Integrations allow you to extend the capabilities of Dub and seamlessly connect with third-party platforms and services. By leveraging these integrations, you can enhance your workflows, automate tasks, connect with your favorite tools, and more.
## Official integrations
These are the integrations that are officially supported and actively maintained by Dub.
}
>
Integrate Dub with Zapier
}
>
Integrate Dub with Make.com
}
>
Integrate Dub with Stripe
}
>
Integrate Dub with Shopify
}
>
Integrate Dub with Segment
}
>
Integrate Dub with Wordpress
}
>
Integrate Dub with Raycast
}
>
Integrate Dub with Slack
## Building your own integrations
You can build your own integrations with Dub's link infrastructure using our [API](/api-reference/introduction).
1. Read the documentation on how to [create links](/api-reference/endpoint/create-a-link) or [track sale conversions](/api-reference/endpoint/track-sale).
2. Learn how to [integrate Dub into your application](/integrations/quickstart).
3. [Reach out to us](https://dub.co/contact/support) to feature your integration in the integrations marketplace.
## Integration webhooks
Dub also supports webhooks for integrations. You can learn more about them in the [webhooks](/concepts/webhooks/introduction) documentation.
Here's a list of the webhooks that Dub supports:
* [`link.created`](/concepts/webhooks/event-types#link-created) – when a new link is created
* [`link.updated`](/concepts/webhooks/event-types#link-updated) – when a link is updated
* [`link.deleted`](/concepts/webhooks/event-types#link-deleted) – when a link is deleted
* [`link.clicked`](/concepts/webhooks/event-types#link-clicked) – when a link is clicked
* [`lead.created`](/concepts/webhooks/event-types#lead-created) – when a new lead is created
* [`sale.created`](/concepts/webhooks/event-types#sale-created) – when a new sale is created
* [`partner.enrolled`](/concepts/webhooks/event-types#partner-enrolled) – when a new partner is enrolled in your program
# Build your own integration
Source: https://dub.co/docs/integrations/quickstart
Learn how to authenticate users with OAuth 2.0. for your Dub integration.
Integrations allow you to extend the capabilities of Dub and seamlessly connect with third-party platforms and services.
You can build your own integrations with Dub using our [API](/api-reference/introduction).
1. Read the documentation on how to [create links](/api-reference/endpoint/create-a-link) or [track sale conversions](/api-reference/endpoint/track-sale).
2. Learn how to [integrate Dub into your application](/integrations/quickstart).
3. [Reach out to us](https://dub.co/contact/support) to feature your integration in the integrations marketplace.
In this guide, you will learn how to create and manage integrations on Dub, allowing you to incorporate Dub's link attribution platform into your application.
## Integrating via OAuth 2.0 (recommended)
Dub supports OAuth 2.0 authentication, which is **recommended** if you build integrations extending Dub's functionality.
We recommend you use a OAuth client library to integrate the OAuth flow. You can find recommended libraries in a variety of programming languages [here](https://oauth.net/code/).
### Set up OAuth 2.0
Here is a step-by-step guide on how to set up OAuth 2.0 authentication with Dub.
* Go to the [OAuth Apps tab](https://app.dub.co/settings/oauth-apps) in your workspace.
* Click on **Create OAuth App**.
* Fill in the required fields to create an OAuth2 application.
When you want to authenticate a user, you need to redirect them to the Dub OAuth authorization URL.
```
GET https://app.dub.co/oauth/authorize
```
Parameters:
| Property | Description |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `client_id` | The client ID of your OAuth application. |
| `redirect_uri` | The URL to redirect the user to after they authorize the application. Make sure this URL is registered in your OAuth application. |
| `response_type` | Expected response type. It should be `code`. |
| `scope` | A space separated list of scopes that you want to request access to. Read more about scopes [here](#scopes). |
| `state` | The state parameter to prevent against CSRF attacks. Read more about it [here](https://auth0.com/docs/protocols/state-parameters) |
An example URL would look like this:
```
GET https://app.dub.co/oauth/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI&response_type=code&scope=SOME_SCOPE&state=SOME_STATE
```
The `code` parameter is returned in the query string when the user is redirected back to your application. You can exchange this code for an access token by making a POST request to the Dub OAuth token URL.
```
POST https://api.dub.co/oauth/token
```
Here's an example using the fetch API:
```js
await fetch("https://api.dub.co/oauth/token", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
code: "YOUR_AUTHORIZATION_CODE",
client_id: "YOUR_CLIENT_ID",
client_secret: "YOUR_CLIENT_SECRET",
redirect_uri: "YOUR_REDIRECT_URI",
grant_type: "authorization_code",
}),
});
```
The `Content-Type` header should be set to `application/x-www-form-urlencoded`.
Parameters:
| Property | Description |
| --------------- | ---------------------------------------------------------------------------- |
| `code` | The code you received when the user was redirected back to your application. |
| `client_id` | The client ID of your OAuth application. |
| `client_secret` | The client secret of your OAuth application. |
| `redirect_uri` | The same redirect URI you used in the authorization URL. |
| `grant_type` | The grant type. It should be `authorization_code`. |
Response:
After a successful request, you will receive a JSON response with the access token.
```json
{
"access_token": "dub_access_token_ae8ebf6f97e6200d886ef48a5...",
"refresh_token": "7f5acfbe14bca0a20fe6e430ddb7bb494eed160bd...",
"token_type": "Bearer",
"expires_in": 7200,
"scope": "links.write tags.write domains.read"
}
```
We recommend using the [PKCE](https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow-with-pkce) flow for native desktop or mobile application or a single-page app (SPA) where the `client_secret` cannot be hidden.
With PKCE, the `client_secret` is **never sent to the authorization server**, preventing the `client_secret` from being leaked from the client application.
Instead of using the `client_secret`, you will need to generate a `code_verifier` and `code_challenge` and use them to exchange for an access token.
For example [Dub Raycast extension](https://github.com/raycast/extensions/tree/main/extensions/dub) uses PKCE to authenticate users.
Once you have obtained a valid access token, you can use it to make requests to the Dub API.
You can initialize [Dub SDK](/sdks/overview) with the access token.
Here is an example of how you can create a link using the [Dub TypeScript SDK](/sdks/typescript):
```javascript
import { Dub } from "dub";
const dub = new Dub({
token: ,
});
const link = await dub.links.create({
url: "https://google.com",
});
```
Or pass the access token in the header: `Authorization: Bearer `
```shell
curl --request POST \
--url https://api.dub.co/links \
--header 'Authorization: Bearer ' \
--header 'Content-Type: application/json'
```
Dub access tokens are short-lived, depending on the `expires_in` value (the default value is **7,200 seconds**, or **2 hours**). Dub will respond with `401 Unauthorized` if you try to use an expired access token.
To refresh the access token, you need to make a POST request to the Dub OAuth token URL with the `refresh_token` you obtained when exchanging the code for an `access_token`.
```
POST https://api.dub.co/oauth/token
```
The `Content-Type` header should be set to `application/x-www-form-urlencoded`.
Parameters:
| Property | Description |
| --------------- | ---------------------------------------------------------------------------- |
| `client_id` | The client ID of your OAuth application. |
| `client_secret` | The client secret of your OAuth application. |
| `grant_type` | The grant type. It should be `refresh_token`. |
| `refresh_token` | The refresh token you received when exchanging the code for an access token. |
Response:
After a successful request, you will receive a JSON response with the new access token.
```json
{
"access_token": "dub_access_token_ae8ebf6f97e6200d886ef48a5...",
"refresh_token": "7f5acfbe14bca0a20fe6e430ddb7bb494eed160bd...",
"token_type": "Bearer",
"expires_in": 7200,
"scope": "links.write tags.write domains.read"
}
```
This will invalidate the old access token and refresh token.
### Scopes
You can request access to specific scopes when redirecting users to the Dub OAuth authorization URL. Scopes are permissions that the user needs to grant to your application.
Dub supports the following scopes for OAuth 2.0:
| Scope | Description |
| ------------------ | ------------------------------------------------------------------- |
| `workspaces.read` | Read access to workspaces. |
| `workspaces.write` | Write access to workspaces. |
| `links.read` | Read access to links. |
| `links.write` | Write access to links. |
| `tags.read` | Read access to tags. |
| `tags.write` | Write access to tags. |
| `analytics.read` | Read access to analytics. |
| `domains.read` | Read access to domains. |
| `domains.write` | Write access to domains. |
| `user.read` | Read access to user information. This scope is included by default. |
### Examples
See the full example on GitHub.
## Integrating via API keys (not recommended)
Dub also supports API key authentication; however, it is **not recommended** for building integrations. It should only be used for internal integrations or personal projects that do not require user consent.
Learn more about [API Keys](/api-reference/tokens).
# Introduction
Source: https://dub.co/docs/introduction
[Dub](https://dub.co) is the modern link attribution platform for you to [create short links](/concepts/links/introduction), [track conversion analytics](/conversions/quickstart), and [run affiliate programs](/partners/quickstart).
Whether you are:
* a marketer looking to track the success of your campaigns
* a creator looking to measure the impact of your content
* a founder looking to [start an affiliate program](/partners/quickstart)
* a developer looking to [integrate link analytics](concepts/analytics/introduction) into your application
We've got you covered.
## Key features
Dub is more than just a link shortener – we offer a suite of tools to help you measure attribution and grow your business with partnerships.
Here are some of the features that Dub offers:
* [Create short links](/concepts/links/introduction) (including [bulk link creation](/concepts/links/bulk-operations))
* [Real-time link analytics](/concepts/analytics/introduction)
* [Conversion tracking](/conversions/quickstart)
* [Partner programs](/partners/quickstart) (e.g. referral/affiliate programs)
* [Webhooks](/concepts/webhooks/introduction)
* [Integrations](https://dub.co/integrations) + [building custom integrations](/integrations/quickstart)
* [REST API](/api-reference/introduction) + [native SDKs](/sdks/overview) for multiple languages
## Getting started
To get started with Dub, you can do any of the following:
Learn how to use Dub's API to interact with Dub programmatically
Learn how to integrate Dub with your favorite tools
Follow our quickstart guide to get started with conversion tracking
Build powerful referral/affiliate programs with white-labeling and 1-click
payouts
## Stay up to date
Learn about the latest features and updates from Dub
Follow us on X/Twitter to get notified when we release new features
# Local development
Source: https://dub.co/docs/local-development
A guide on how to run Dub's codebase locally.
## Introduction
Dub's codebase is set up in a monorepo (via [Turborepo](https://turbo.build/repo)) and is fully [open-source on GitHub](https://github.com/dubinc/dub).
Here's the monorepo structure:
```
apps
├── web
packages
├── cli
├── email
├── embeds
├── prisma
├── stripe-app
├── tailwind-config
├── tinybird
├── tsconfig
├── ui
├── utils
```
The `apps` directory contains the code for:
* `web`: The entirety of Dub's application ([app.dub.co](https://app.dub.co)) + our link redirect infrastructure.
The `packages` directory contains the code for:
* `cli`: A CLI for easily shortening URLs with the Dub API.
* `email`: Dub's email application with function to send emails and templates.
* `embeds`: A package used embed Dub's referral dashboard.
* `prisma`: Prisma Configuration for Dub's web-app.
* `stripe-app`: The Stripe app for dub conversions.
* `tailwind-config`: The Tailwind CSS configuration for Dub's web app.
* `tinybird`: Dub's Tinybird configuration.
* `tsconfig`: The TypeScript configuration for Dub's web app.
* `ui`: Dub's UI component library.
* `utils`: A collection of utility functions and constants used across Dub's codebase.
## How `app.dub.co` works
Dub's web app is built with [Next.js](https://nextjs.org) and [TailwindCSS](https://tailwindcss.com).
It also utilizes code from the `packages` directory, specifically the `@dub/ui` and `@dub/utils` packages.
All of the code for the web app is located in here: [`main`/apps/web/app/app.dub.co](https://github.com/dubinc/dub/tree/main/apps/web/app/app.dub.co). This is using the Next.js [route group pattern](https://nextjs.org/docs/app/building-your-application/routing/route-groups).
There's also the API server, which is located in here: [`main`/apps/web/app/api](https://github.com/dubinc/dub/tree/main/apps/web/app/api)
When you run `pnpm dev` to start the development server, the app will be available at [http://localhost:8888](http://localhost:8888). The reason we use `localhost:8888` and not `app.localhost:8888` is because Google OAuth doesn't allow you to use localhost subdomains.
## How link redirects work on Dub
Link redirects on Dub are powered by [Next.js Middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware).
To handle high traffic, we use Redis to cache every link's metadata when it's first created. This allows us to serve redirects without hitting our MySQL database.
Here's the code that powers link redirects: [`main`/apps/web/lib/middleware/link.ts](https://github.com/dubinc/dub/blob/main/apps/web/lib/middleware/link.ts)
## Running Dub locally
To run Dub locally, you'll need to set up the following:
* A [Tinybird](https://www.tinybird.co/) account
* An [Upstash](https://upstash.com/) account
* A [PlanetScale](https://planetscale.com/)-compatible MySQL database
Watch this video from our friends at Tinybird to learn how to set up Dub locally:
## Step 1: Local setup
First, you'll need to clone the Dub repo and install the dependencies.
First, clone the [Dub repo](https://d.to/github) into a public GitHub repository.
```bash Terminal
git clone https://github.com/dubinc/dub.git
```
Run the following command to install the dependencies:
```bash Terminal
pnpm i
```
Execute the command below to compile all internal packages:
```bash Terminal
pnpm -r --filter "./packages/**" build
```
Copy the `.env.example` file from `./apps/web` to `.env` by executing the following command from `apps/web`:
```bash Terminal
cp ./apps/web/.env.example .env
```
You'll be updating this `.env` file with your own values as you progress through the setup.
## Step 2: Set up Tinybird Clickhouse database
Next, you'll need to set up the [Tinybird](https://tinybird.co) Clickhouse database. This will be used to store time-series click events data.
In your [Tinybird](https://tinybird.co/) account, create a new Workspace.
Copy your `admin` [Auth Token](https://www.tinybird.co/docs/concepts/auth-tokens.html). Paste this token as the `TINYBIRD_API_KEY` environment variable in your `.env` file.
Alternatively, you can set up a [local Tinybird container](https://www.tinybird.co/docs/cli/local-container) for local development.
In your newly-cloned Dub repo, navigate to the `packages/tinybird` directory.
If you have `brew`, install `pipx` by running `brew install pipx`. If not, you can check [installation guide](https://pipx.pypa.io/stable/installation/) for other options. After that, install the Tinybird CLI with `pipx install tinybird-cli` (requires Python >= 3.8).
Run `tb auth --interactive` and paste your `admin` Auth Token.
Run `tb push` to publish the datasource and endpoints in the `packages/tinybird` directory. You should see the following output (truncated for brevity):
```bash Terminal
$ tb push
** Processing ./datasources/click_events.datasource
** Processing ./endpoints/clicks.pipe
...
** Building dependencies
** Running 'click_events'
** 'click_events' created
** Running 'device'
** => Test endpoint at https://api.us-east.tinybird.co/v0/pipes/device.json
** Token device_endpoint_read_8888 not found, creating one
** => Test endpoint with:
** $ curl https://api.us-east.tinybird.co/v0/pipes/device.json?token=p.ey...NWeaoTLM
** 'device' created
...
```
You will then need to update your [Tinybird API base URL](https://www.tinybird.co/docs/api-reference/api-reference.html#regions-and-endpoints) to match the region of your database.
From the previous step, take note of the **Test endpoint** URL. It should look something like this:
```bash Terminal
Test endpoint at https://api.us-east.tinybird.co/v0/pipes/device.json
```
Copy the base URL and paste it as the `TINYBIRD_API_URL` environment variable in your `.env` file.
```bash Terminal
TINYBIRD_API_URL=https://api.us-east.tinybird.co
```
## Step 3: Set up Upstash Redis database
Next, you'll need to set up the [Upstash](https://upstash.com) Redis database. This will be used to cache link metadata and serve link redirects.
In your [Upstash account](https://console.upstash.com/), create a new database.
For better performance & read times, we recommend setting up a global database with several read regions.

Once your database is created, copy the `UPSTASH_REDIS_REST_URL` and `UPSTASH_REDIS_REST_TOKEN` from the **REST API** section into your `.env` file.

Navigate to the [QStash tab](https://console.upstash.com/qstash) and copy the `QSTASH_TOKEN`, `QSTASH_CURRENT_SIGNING_KEY`, and `QSTASH_NEXT_SIGNING_KEY` from the **Request Builder** section into your `.env` file.

If you're planning to run Qstash-powered background jobs locally, you'll need to set up an Ngrok tunnel to expose your local server to the internet.
Follow [these steps](https://ngrok.com/docs/getting-started/) to setup `ngrok`, and then run the following command to start an Ngrok tunnel at port `8888`:
```bash Terminal
ngrok http 8888
```
Copy the `https` URL and paste it as the `NEXT_PUBLIC_NGROK_URL` environment variable in your `.env` file.
## Step 4: Set up PlanetScale MySQL database
Next, you'll need to set up a [PlanetScale](https://planetscale.com/)-compatible MySQL database. This will be used to store user data and link metadata. There are two options:
### Option 1: Local MySQL database with PlanetScale simulator (recommended)
You can use a local MySQL database with a PlanetScale simulator. This is the recommended option for local development since it's 100% free.
Prerequisites:
* [Docker](https://www.docker.com/products/docker-desktop)
* [Docker Compose](https://docs.docker.com/compose/install/)
In the terminal, navigate to the `apps/web` directory and run the following command to start the Docker Compose stack:
```bash Terminal
docker-compose up
```
This will start two containers: one for the MySQL database and another for the PlanetScale simulator.
Ensure the following credientials are added to your `.env` file:
```
DATABASE_URL="mysql://root:@localhost:3306/planetscale"
PLANETSCALE_DATABASE_URL="http://root:unused@localhost:3900/planetscale"
```
Here, we are using the open-source [PlanetScale simulator](https://github.com/mattrobenolt/ps-http-sim) so the application can continue to use the `@planetscale/database` SDK.
While we're using two different values in local development, in production or staging environments, you'll only need the `DATABASE_URL` value.
In the terminal, navigate to the `apps/web` directory and run the following command to generate the Prisma client:
```bash Terminal
pnpm run prisma:generate
```
Then, create the database tables with the following command:
```bash Terminal
pnpm run prisma:push
```
The docker-compose setup includes Mailhog, which acts as a mock SMTP server
and shows received emails in a web UI. You can access the Mailhog web
interface at [http://localhost:8025](http://localhost:8025). This is useful
for testing email functionality without sending real emails during local
development.
### Option 2: PlanetScale hosted database
PlanetScale recently [removed their free
tier](https://planetscale.com/blog/planetscale-forever), so you'll need to pay
for this option. A cheaper alternative is to use a [MySQL database on
Railway](https://railway.app/template/mysql) (\$5/month).
In your [PlanetScale account](https://app.planetscale.com/), create a new database.
Once your database is created, you'll be prompted to select your language or Framework. Select **Prisma**.

Then, you'll have to create a new password for your database. Once the password is created, scroll down to the **Add credentials to .env** section and copy the `DATABASE_URL` into your `.env` file.

In the terminal, navigate to the `apps/web` directory and run the following command to generate the Prisma client:
```bash Terminal
pnpm run prisma:generate
```
Then, create the database tables with the following command:
```bash Terminal
pnpm run prisma:push
```
## Step 5: Set up Mailhog
To view emails sent from your application during local development, you'll need to set up [Mailhog](https://github.com/mailhog/MailHog).
If you've already run `docker compose up` as part of the database setup, you
can skip this step. Mailhog is included in the Docker Compose configuration
and should already be running.
Run the following command to pull the Mailhog Docker image:
```bash Terminal
docker pull mailhog/mailhog
```
Start the Mailhog container with the following command:
```bash Terminal
docker run -d -p 8025:8025 -p 1025:1025 mailhog/mailhog
```
This will run Mailhog in the background, and the web interface will be available at [http://localhost:8025](http://localhost:8025).
## Step 6: Set NextAuth secret
Generate a secret by visiting [https://generate-secret.vercel.app/32](https://generate-secret.vercel.app/32). Set the value of `NEXTAUTH_SECRET` in `.env` to this value.
## Step 7: Start the development server
Finally, you can start the development server. This will build the packages + start the app servers.
```bash Terminal
pnpm dev
```
The web app (`apps/web`) will be available at [localhost:8888](http://localhost:8888).
### Testing your shortlinks locally
Use the following url structure to ensure event tracking is working, and to populate analytics data, replacing `` with the shortlink key you've created.
```
http://dub.localhost:8888/
```
# Quickstart
Source: https://dub.co/docs/partners/quickstart
Learn how to set up your referral/affiliate program with Dub Partners.
Dub Partners require a [Business plan](https://dub.co/pricing) subscription or
higher.
[Dub Partners](https://dub.co/help/article/dub-partners) lets you build powerful, scalable referral and affiliate programs with 1-click global payouts and white-labeling functionality.
In this guide, we'll walk you through the steps to get started with Dub Partners:
1. [Install the `@dub/analytics` client-side SDK](#step-1-install-the-dub-analytics-client-side-sdk)
2. [Install the Dub server-side SDK + track conversion events](#step-2-install-the-dub-server-side-sdk--track-conversion-events)
3. [Set up your partner program](#step-3-set-up-your-partner-program)
## Step 1: Install the `@dub/analytics` client-side SDK
First, you'll need to install the [@dub/analytics client-side SDK](/sdks/client-side/introduction).
This script detects the `dub_id` query parameter and storing it as a first-party cookie, which will be used to attribute subsequent conversion events to the original link.
On top of that, it also:
* lets you [track clicks on the client-side](/sdks/client-side/features/client-side-click-tracking) using query parameters (e.g. `?via=john`)
* [automatically fetch the partner and discount data](/sdks/client-side/features/client-side-click-tracking#automatically-fetching-partner-and-discount-data) for a given link – which is helpful for displaying [dual-sided incentives](https://dub.co/help/article/dual-sided-incentives)
You can install the `@dub/analytics` script in several different ways:
Add Dub Analytics to your React app
Add Dub Analytics to your website
}
href="https://dub.co/docs/sdks/client-side/installation-guides/framer"
>
Add Dub Analytics to your Framer site
Add Dub Analytics to your Shopify store
Add Dub Analytics to your WP site
Add Dub Analytics to your Webflow site
You can **verify the installation** with the following tests:
1. Open the browser console and type in `_dubAnalytics` – if the script is installed correctly, you should see the `_dubAnalytics` object in the console.
2. Add the `?dub_id=test` query parameter to your website URL and make sure that the `dub_id` cookie is being set in your browser.
If both of these checks pass, the script is installed correctly. Otherwise, please make sure:
* The analytics script was added to the `` section of the page
* If you're using a content delivery network (CDN), make sure to purge any cached content
Once the script is installed, make sure to verify that [client-side click tracking](/sdks/client-side/features/client-side-click-tracking) is working as expected by checking if the following is true:
1. There is a successful `/track/click` request in your browser's **Network** tab (and no errors in the **Console** tab).
2. The `dub_id` cookie is being set upon a successful `/track/click` request.
3. The click tracked correctly in the [**Events**](https://app.dub.co/events) tab of your Dub workspace.
In case you missed it, you'll also need to allowlist your site's domain to ensure the client-side click events are ingested by Dub. To do that, navigate to your [workspace's Analytics settings page](https://app.dub.co/settings/analytics) and add your site's domain to the **Allowed Hostnames** list.
When testing things out locally, you can add `localhost` to the **Allowed
Hostnames** list temporarily. This will allow local events to be ingested by
Dub. Don't forget to remove it once you're ready to go live!
## Step 2: Install the Dub server-side SDK + track conversion events
If you are using Shopify, you can skip this step since our Shopify app will
automatically track conversions for you. Read the [Shopify integration
guide](https://dub.co/docs/conversions/sales/shopify) for more information.
Dub uses server-side event tracking to track conversions, which is more reliable than client-side tracking. Depending on which framework you're using, you can use our [native SDKs](/sdks/overview):
TypeScript library for the Dub API
Go library for the Dub API
Python library for the Dub API
Ruby library for the Dub API
If you're using a framework that isn't listed, you can use the Dub REST API to track events on the server-side:
* [`POST /track/lead`](https://dub.co/docs/api-reference/endpoint/track-lead)
* [`POST /track/sale`](https://dub.co/docs/api-reference/endpoint/track-sale)
Once you install the SDKs, you can start tracking conversion events.
### Tracking lead events
The first event you'll want to track is a `lead` event. This happens when a user performs an action that indicates interest in your product or service. This could be anything from:
* Signing up for an account
* Adding a product to cart
* Joining a mailing list
Our most common lead event is `Sign Up`, which happens when a user signs up for an account. Depending on which authentication framework you're using, here are a few examples of how to send `Sign Up` lead events:
The lead event will serve as the source of truth for the customer's identity and which link they came from. This means that all subsequent actions performed by the customer (e.g. upgrading their plan, purchasing a product) will automatically be attributed to the original link.
To learn more about tracking lead events with Dub, refer to the following resources:
Read the full guide on tracking lead events with Dub
View the full list of attributes you can pass when sending a lead event
### Tracking sale events
The second event you'll want to send is a `sale` event. This happens when a user purchases your product or service. This could be anything from:
* Subscribing to a paid plan
* Usage expansion (upgrading from one plan to another)
* Purchasing a product
Depending on which payment processor you're using, we offer native integrations for the following:
Alternatively, you can also send sale events manually using [our SDKs](/sdks/overview) or the [`POST /track/sale` API endpoint](https://dub.co/docs/api-reference/endpoint/track-sale).
To learn more about tracking sale events with Dub, refer to the following resources:
Read the full guide on tracking sale events with Dub
View the full list of attributes you can pass when sending a sale event
## Step 3: Set up your partner program
Once you have conversion tracking up and running, you can follow these steps to set up your partner program:
1. Go through the [program onboarding flow](https://app.dub.co/program/new) to create your program.
* If you're migrating from another platform, you can also import your partners and historical conversions (e.g. [from Rewardful](https://dub.co/help/article/migrating-from-rewardful)).
2. [Connect your bank account](https://dub.co/help/article/how-to-set-up-bank-account) for partner payouts.
3. [Invite your partners](https://dub.co/help/article/inviting-partners) to join your program + set up your [program application form](https://dub.co/help/article/inviting-partners#via-a-branded-application-form) for future partners.
4. Create [custom reward rules](https://dub.co/help/article/partner-rewards) and [dual-sided incentives](https://dub.co/help/article/dual-sided-incentives) to incentivize partners to share your link with others.
5. Set up a [whitelabeled referral dashboard](/partners/white-labeling) that lives directly inside your app so your users can automatically enroll in your partner program without leaving your app.
# White-labeling
Source: https://dub.co/docs/partners/white-labeling
Learn how to create a white-labeled referral dashboard with Dub Partners.
Dub Partners whitelabeling require an [Advanced plan](https://dub.co/pricing)
subscription or higher.
With [Dub Partners](/partners/quickstart), you can build a white-labeled referral dashboard that lives directly inside your app in just a few lines of code.
This way, your users can automatically enroll in your partner program **without needing to leave your app and sign up on a third-party platform**.
In this guide, we'll walk you through the steps to get started with the Dub Referrals Embed.
## Step 1: Generate embed token
First, you need to create a server API route that generates a public token, which will be used by the Dub Referrals Embed to fetch real-time conversions and earnings data from the client-side.
### Using server-side SDKs
If you're using our [server-side SDKs](/sdks/overview), you can generate an embed token using the `dub.embedTokens.referrals` method.
```ts TypeScript
const { publicToken } = await dub.embedTokens.referrals({
tenantId: user.id, // the user's ID within your application
partner: {
name: user.name, // the user's name
email: user.email, // the user's email
image: user.image, // the user's image/avatar
tenantId: user.id, // the user's ID within your application
},
});
```
```python Python
from dub import Dub
with Dub(
token="DUB_API_KEY",
) as d_client:
res = d_client.embed_tokens.referrals(request={
"tenant_id": user.id, # the user's ID within your application
"partner": {
"name": user.name, # the user's name
"email": user.email, # the user's email
"image": user.image, # the user's image/avatar
"tenant_id": user.id, # the user's ID within your application
},
})
# Handle response
print(res.public_token)
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity("DUB_API_KEY"),
)
res, err := s.EmbedTokens.Referrals(ctx, &operations.CreateReferralsEmbedTokenRequestBody{
TenantID: user.ID, // the user's ID within your application
Partner: &operations.Partner{
Name: user.Name, // the user's name
Email: user.Email, // the user's email
Image: user.Image, // the user's image/avatar
TenantID: user.ID, // the user's ID within your application
},
})
if err != nil {
log.Fatal(err)
}
if res != nil {
// Handle response
log.Printf("Public token: %s", res.PublicToken)
}
}
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()
->setSecurity('DUB_API_KEY')
->build();
$request = new Operations\CreateReferralsEmbedTokenRequestBody(
tenantId: $user->id, // the user's ID within your application
partner: new Operations\Partner(
name: $user->name, // the user's name
email: $user->email, // the user's email
image: $user->image, // the user's image/avatar
tenantId: $user->id, // the user's ID within your application
),
);
$response = $sdk->embedTokens->referrals(
request: $request
);
if ($response->object !== null) {
// Handle response
echo $response->object->publicToken;
}
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new(
security: ::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
),
)
req = ::OpenApiSDK::Operations::CreateReferralsEmbedTokenRequestBody.new(
tenant_id: user.id, # the user's ID within your application
partner: ::OpenApiSDK::Operations::Partner.new(
name: user.name, # the user's name
email: user.email, # the user's email
image: user.image, # the user's image/avatar
tenant_id: user.id, # the user's ID within your application
),
)
res = s.embed_tokens.referrals(req)
if !res.object.nil?
# Handle response
puts res.object.public_token
end
```
### Using REST API
If you're not using our server-side SDKs, you can generate an embed token using our REST API instead (via the [`POST /tokens/embed/referrals`](/api-reference/endpoint/create-referrals-embed-token) endpoint).
```js JavaScript
const response = await fetch("https://api.dub.co/tokens/embed/referrals", {
method: "POST",
body: JSON.stringify({
tenantId: user.id, // the user's ID within your application
partner: {
name: user.name, // the user's name
email: user.email, // the user's email
image: user.image, // the user's image/avatar
tenantId: user.id, // the user's ID within your application
},
}),
});
const data = await response.json();
const { publicToken } = data;
```
Refer to the [full API reference](/api-reference/endpoint/create-referrals-embed-token) to learn more about the properties you can pass to the `POST /tokens/embed/referrals` endpoint.
## Step 2: Install the embed
Then, with the `publicToken` from Step 1, you can install and initialize the Dub Referrals Embed. There are two ways to do this:
### React component
First, install the [NPM package](https://www.npmjs.com/package/@dub/embed-react):
```bash npm
npm install @dub/embed-react
```
```bash yarn
yarn add @dub/embed-react
```
```bash pnpm
pnpm add @dub/embed-react
```
```bash bun
bun add @dub/embed-react
```
Then use the component in your React application:
```tsx
import { useState, useEffect } from "react";
import { DubEmbed } from "@dub/embed-react";
export default function App() {
const [publicToken, setPublicToken] = useState("");
useEffect(() => {
const fetchPublicToken = async () => {
// fetching from the server API route you created in Step 1
const response = await fetch("/api/embed-token");
const data = await response.json();
setPublicToken(data.publicToken);
};
fetchPublicToken();
}, []);
if (!publicToken) {
return
Loading...
;
}
return ;
}
```
### Iframe embed
Alternatively, if you're not using React (or you're not on React `v18.2.0` or higher), you can add the iframe directly to your HTML:
```tsx
import { useState, useEffect } from "react";
export default function App() {
const [publicToken, setPublicToken] = useState("");
useEffect(() => {
const fetchPublicToken = async () => {
// fetching from the server API route you created in Step 1
const response = await fetch("/api/embed-token");
const data = await response.json();
setPublicToken(data.publicToken);
};
fetchPublicToken();
}, []);
if (!publicToken) {
return
Loading...
;
}
return (
);
}
```
## Embed options
The Dub Referrals Embed supports the following options for styling and behavior:
The type of embed to use. In this case, we're using the `referrals` type.
The theme of the embed.
Available options:
* `backgroundColor`: The background color of the embed.
Depending on the embed type, you can use the following examples to initialize the embed options:
```tsx React component
import { DubEmbed } from "@dub/embed-react";
const publicToken = "...";
;
```
```tsx iFrame embed
const publicToken = "...";
const iframeUrl = "https://app.dub.co/embed/referrals";
const iframeParams = new URLSearchParams({
token: publicToken,
theme: "light",
themeOptions: JSON.stringify({ backgroundColor: "#F5F5F5" }),
});
;
```
## Example Apps
See the full example on GitHub.
# Client-side click-tracking
Source: https://dub.co/docs/sdks/client-side/features/client-side-click-tracking
Track clicks on the client-side using query parameters
With the [`@dub/analytics` script](/sdks/client-side/introduction), you can track clicks on the client-side using query parameters (e.g. `?via=john`, `?ref=jane`).
A few use cases include:
* You're using [Dub Partners](https://dub.co/help/article/dub-partners) with [dual-sided incentives](https://dub.co/help/article/dual-sided-incentives) and want to display a banner that says: *"John referred you to Acme and gave you 25% off"*
* You have dynamically generated referral pages (e.g. [Tesla](https://www.tesla.com/referral/peeroke520149)) and want to track clicks [using a `trackClick()` function](#manually-tracking-clicks-with-the-trackclick-function) inside your application code.
* You are migrating from an existing affiliate management platform (e.g. [Rewardful](https://dub.co/help/article/migrating-from-rewardful)) that uses query parameters to track conversions.
## Quickstart
Here's how you can enable client-side click-tracking with the `@dub/analytics` script:
First, you'll need to [add a custom short link domain](https://dub.co/help/article/how-to-add-custom-domain) to your Dub workspace. You can use a domain you already own, or leverage our [free .link domain offer](https://dub.co/help/article/free-dot-link-domain) to register a custom domain like `yourcompany.link` for free.
This is the domain that you'll use for your short links on Dub.
Then, you'll need to allowlist your site's domain to allow the client-side click events to be ingested by Dub. To do that, navigate to your [workspace's Analytics settings page](https://app.dub.co/settings/analytics) and add your site's domain to the **Allowed Hostnames** list.
When testing things out locally, you can add `localhost` to the **Allowed
Hostnames** list temporarily. This will allow local events to be ingested by
Dub. Don't forget to remove it once you're ready to go live!
Next, install the Dub [client-side SDK](/sdks/client-side/introduction) and initialize it with the domain you added in the previous step.
```typescript React/Next.js
// install the package (e.g. npm install @dub/analytics)
import { Analytics as DubAnalytics } from "@dub/analytics/react";
export default function App() {
return (
{/* Your app code here */}
);
}
```
```javascript Other Frameworks
// include this script tag in your HTML Head tag
```
Here's the full list of parameters you can pass to the script:
The base URL for the Dub API. This is useful for [setting up reverse
proxies](/sdks/client-side/features/reverse-proxy-support) to avoid
adblockers.
The attribution model to use for the analytics event. The following
attribution models are available:
* `first-click`: The first click model
gives all the credit to the first touchpoint in the customer journey.
* `last-click`: The last click model gives all the credit to the last
touchpoint in the customer journey.
Specifies the value for the `Domain` Set-Cookie attribute. This is useful
for cross-domain tracking. Example: `.example.com`
Specifies the `Date` object to be the value for the `Expires` Set-Cookie
attribute. Example: `new Date('2024-12-31')`
Specifies the number (in days) to be the value for the `Expires`
Set-Cookie attribute. Example: `90`
Specifies the value for the `Path` Set-Cookie attribute. By default, the
path is considered the "default path". Example: `/`
Configure the domains that Dub will track. The following properties are available:
The Dub custom domain for [referral program client-side click tracking](http://d.to/clicks/refer)
(previously `shortDomain`).
Example: `refer.dub.co`
The Dub short domain for tracking site visits.
Example: `site.dub.co`
An array of domains for cross-domain tracking. When configured, the existing
`dub_id` cookie will be automatically appended to all outbound links
targeting these domains to enable cross-domain tracking across different
applications.
Example: `["dub.sh", "git.new"]`
The query parameter to listen to for client-side click-tracking (e.g.
`?via=abc123`).
Custom properties to pass to the script tag. Refer to
[MDN](https://developer.mozilla.org/en-US/docs/Web/API/HTMLScriptElement) for
all available options.
To avoid ad-blockers from blocking your click-tracking requests, we recommend setting up a reverse proxy.
Refer to the [Reverse-proxy support](/sdks/client-side/features/reverse-proxy-support) guide to learn how to set one up.
To verify that your click-tracking is working, run your website locally and append the URL with the unique key of your short link
For example, if your short link is `https://go.example.com/abc123`, you'll need to append `?via=abc123` to the URL.
Once you've done that, check if the following is true:
1. There is a successful `/track/click` request in your browser's **Network** tab (and no errors in the **Console** tab).
2. The `dub_id` cookie is being set upon a successful `/track/click` request.
3. The click tracked correctly in the [**Events**](https://app.dub.co/events) tab of your Dub workspace.
## Automatically fetching partner and discount data
If you're using [Dub Partners](/partners/quickstart) with [dual-sided incentives](https://dub.co/help/article/dual-sided-incentives), the script will automatically fetch the partner and discount data for you when someone lands on your site via a valid referral link.
This data will be stored as a JSON-stringified object in the `dub_partner_data` cookie in the following format:
```json
{
"clickId": "xxx", // unique ID of the click event
"partner": {
"id": "pn_xxx", // unique ID of the partner on Dub
"name": "John Doe", // name of the partner
"image": "https://example.com/john.png" // avatar of the partner
},
"discount": {
"id": "disc_xxx", // unique ID of the discount on Dub
"amount": 25, // discount amount (either a percentage or a fixed amount)
"type": "percentage", // type of the discount (either "percentage" or "fixed")
"maxDuration": 3, // maximum duration of the discount in months
"couponId": "XZuejd0Q", // Stripe coupon code
"couponTestId": "2NMXz81x" // Stripe test coupon ID
}
}
```
The `name` and `image` fields in the partner data are URL-encoded when stored
in the cookie. Make sure to decode these values using `decodeURIComponent()`
when accessing them from the cookie. However, if you're using `js-cookie` as
shown in the example below, the decoding is handled automatically for you.
To access this cookie data, we recommend using a cookie library like [`js-cookie`](https://github.com/js-cookie/js-cookie) – here's an example:
```js
import Cookies from "js-cookie";
const dubPartnerData = Cookies.get("dub_partner_data");
const { partner, discount } = dubPartnerData ? JSON.parse(dubPartnerData) : {};
// Display a banner that says: _"John referred you to Acme and gave you 25% off"_
return (
{name} referred you to Acme and gave you {amount} {type} off
);
```
Here's an example of how the discount banner will look like:
## Manually tracking clicks with the `trackClick()` function
This is helpful for tracking clicks on:
* Dynamically generated referral pages (e.g. [Tesla](https://www.tesla.com/referral/peeroke520149))
* Dynamic user-generated content/webpages:
* Dub's public analytics dashboards (e.g. [app.dub.co/share/:slug](https://app.dub.co/share/dash_6NSA6vNm017MZwfzt8SubNSZ))
* Cal.com's booking pages (e.g. [cal.com/steven](https://cal.com/steven))
* Tella's video pages (e.g. [tella.tv/video/:slug](https://www.tella.tv/video/cluvcfcfi00tw0fjrgizr4pw2))
This feature is coming soon. If you'd like early access, please [contact
us](https://dub.co/contact/support).
## Differences from server-side click-tracking
Server-side click-tracking is enabled by default for all Dub links and come with the following attributes:
| Attribute | Type | Description |
| :----------- | :------ | :----------------------------------------------------------------------------------------------- |
| `timestamp` | string | The timestamp of the click event |
| `id` | string | The unique ID of the click event |
| `url` | string | The destination URL that the link resolved to – this can vary if geo/device-targeting is enabled |
| `continent` | string | The continent of the user who clicked the link |
| `country` | string | The country of the user who clicked the link |
| `city` | string | The city of the user who clicked the link |
| `device` | string | The device of the user who clicked the link |
| `browser` | string | The browser of the user who clicked the link |
| `os` | string | The operating system of the user who clicked the link |
| `referer` | string | The referrer of the user who clicked the link |
| `refererUrl` | string | The full referrer URL of the user who clicked the link |
| `qr` | boolean | Whether the click event was triggered by a QR code scan |
| `ip` | string | The IP address of the user who clicked the link (non-EU users only) |
These events happen on the server-side and cannot be blocked by browser extensions or ad-blockers, which improves the accuracy of your analytics data.
# Conversion-tracking
Source: https://dub.co/docs/sdks/client-side/features/conversion-tracking
Learn how to use the @dub/analytics script to track conversion events
`@dub/analytics` is a client-side script for [tracking conversion events](/conversions/quickstart) with Dub.
By default, the script handles the detection of the `dub_id` query parameter and storing it as a first-party cookie:
Then, when a conversion event occurs (e.g. a user signs up for an account), you can check for the `dub_id` cookie and attribute the conversion to the original click by [tracking a lead event](/conversions/leads/introduction).
Finally, when the user completes a purchase (e.g. subscribing to a plan, purchasing a product, etc.), you can [track a sale event](/conversions/sales/introduction). Under the hood, Dub will automatically attribute the sale to the original link click.
# Cross-domain tracking
Source: https://dub.co/docs/sdks/client-side/features/cross-domain-tracking
Track conversions across domains
By default, the script already sets the `dub_id` cookie on a **cross-domain level**.
This means that if you have the script installed on your marketing site (e.g. **example.com**), the cookie will also be accessible when your user signs up for your app (e.g. **app.example.com**).
However, if you are installing the script on a subdomain (e.g. **app.example.com**), you will need to set the following option to make sure the cookie is accessible on the apex domain as well (e.g. **example.com**):
```typescript React
```
```html Other
```
The script also supports conversion tracking across *entirely different domains*.
This means that if you have the script installed on a separate domain (e.g. **example.sh**), you can use the `outboundDomains` prop to ensure that the `dub_id` cookie value is automatically appended to all outbound links targeting your main domain (e.g. **example.com**).
```typescript React
// install this script on both domains
```
```html Other
```
For outbound-domains support, you'll need to use the
[`script.outbound-domains.js`](/sdks/client-side/variants#outbound-domains-variant-script-outbound-domains-js)
variant of the script. Learn more about [how script variants
work](/sdks/client-side/variants).
# Reverse-proxy support
Source: https://dub.co/docs/sdks/client-side/features/reverse-proxy-support
Track clicks on the client-side using a reverse proxy
## Tracking clicks via a reverse proxy
To avoid ad-blockers from blocking your click-tracking requests, we recommend setting up a reverse proxy.
Depending on which backend framework you're using, there are a few different ways to do this:
```javascript Next.js
// next.config.js
module.exports = {
async rewrites() {
return [
{
source: "/_proxy/dub/track/:path",
destination: "https://api.dub.co/track/:path",
},
];
},
};
```
```json Vercel
// vercel.json
{
"rewrites": [
{
"source": "/_proxy/dub/track/:path",
"destination": "https://api.dub.co/track/:path"
}
]
}
```
Once you've set up your reverse proxy, don't forget to update the `apiHost` parameter in the `` component to point to your proxy URL.
```typescript React/Next.js
import { Analytics as DubAnalytics } from "@dub/analytics/react";
export default function App() {
return (
{/* Your app code here */}
);
}
```
```javascript Other Frameworks
// include this script tag in your HTML Head tag
```
## Loading the script via a reverse proxy
To avoid ad-blockers from blocking the `@dub/analytics` script, it is recommended to use a reverse proxy.
Depending on which backend framework you're using, there are a few different ways to do this:
```javascript Next.js
// next.config.js
module.exports = {
async rewrites() {
return [
{
source: "/_proxy/dub/script.js",
destination: "https://www.dubcdn.com/analytics/script.js",
},
];
},
};
```
```json Vercel
// vercel.json
{
"rewrites": [
{
"source": "/_proxy/dub/script.js",
"destination": "https://www.dubcdn.com/analytics/script.js"
}
]
}
```
Once you've set up your reverse proxy, don't forget to update the `scriptProps.src` parameter in the `` component to point to your proxy URL.
```typescript React/Next.js
import { Analytics as DubAnalytics } from "@dub/analytics/react";
export default function App() {
return (
{/* Your app code here */}
);
}
```
```javascript Other Frameworks
// include this script tag in your HTML Head tag
```
# Framer
Source: https://dub.co/docs/sdks/client-side/installation-guides/framer
How to add @dub/analytics to your Framer site
With `@dub/analytics`, you can track lead and sale conversions on your Framer site, enabling you to measure the effectiveness of your marketing campaigns.
You can add the `@dub/analytics` script to your Framer website same way you would add Google Analytics script or any other JavaScript code.
Follow these steps to add the script to your site:
* Go to your Framer project and open the **Project Settings** menu.
* Open the **General** tab and scroll down to the **Custom Code** section.
* Paste the Dub analytics script in the **Start of head tag** section.
* Click on the **Save** button to save the changes.
```html
```
If you're using [Dub Partners](/partners/quickstart) for affiliate management, you will also need to set up the `data-domains` property to enable [client-side click-tracking](/sdks/client-side/features/client-side-click-tracking).
```html
```
Read the [client-side click-tracking guide](/sdks/client-side/features/client-side-click-tracking) for more information.
You can **verify the installation** with the following tests:
1. Open the browser console and type in `_dubAnalytics` – if the script is installed correctly, you should see the `_dubAnalytics` object in the console.
2. Add the `?dub_id=test` query parameter to your website URL and make sure that the `dub_id` cookie is being set in your browser.
If both of these checks pass, the script is installed correctly. Otherwise, please make sure:
* The analytics script was added to the `` section of the page
* If you're using a content delivery network (CDN), make sure to purge any cached content
## Concepts
You can pass the following props to the `@dub/analytics` script to customize its behavior:
The base URL for the Dub API. This is useful for [setting up reverse
proxies](/sdks/client-side/features/reverse-proxy-support) to avoid
adblockers.
The attribution model to use for the analytics event. The following
attribution models are available:
* `first-click`: The first click model
gives all the credit to the first touchpoint in the customer journey.
* `last-click`: The last click model gives all the credit to the last
touchpoint in the customer journey.
Custom properties to pass to the cookie. Refer to
[MDN's Set-Cookie documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie) for
all available options.
Specifies the value for the `Domain` Set-Cookie attribute. This is useful
for cross-domain tracking. Example: `.example.com`
Specifies the `Date` object to be the value for the `Expires` Set-Cookie
attribute. Example: `new Date('2024-12-31')`
Specifies the number (in days) to be the value for the `Expires`
Set-Cookie attribute.
For example, to set the cookie window to 60 days (instead of the default 90 days), you can add the following to your script:
```html
```
Specifies the value for the `Path` Set-Cookie attribute. By default, the
path is considered the "default path". Example: `/`
Configure the domains that Dub will track. The following properties are available:
The Dub custom domain for [referral program client-side click tracking](http://d.to/clicks/refer) (previously `data-short-domain`).
Example: `refer.dub.co`
The Dub short domain for tracking site visits.
Example: `site.dub.co`
An array of domains for cross-domain tracking. When configured, the existing `dub_id` cookie
will be automatically appended to all outbound links targeting these domains to enable
cross-domain tracking across different applications.
Example: `"dub.sh, git.new"`
The query parameter to listen to for client-side click-tracking (e.g.
`?via=abc123`).
# Manual Installation
Source: https://dub.co/docs/sdks/client-side/installation-guides/manual
How to add @dub/analytics to your website
With `@dub/analytics`, you can track lead and sale conversions on your website, enabling you to measure the effectiveness of your marketing campaigns.
You can add the `@dub/analytics` script to your website same way you would add Google Analytics script or any other JavaScript code – by adding the `@dub/analytics` script in the `` section of your HTML file.
```html
```
If you're using [Dub Partners](/partners/quickstart) for affiliate management, you will also need to set up the `data-domains` property to enable [client-side click-tracking](/sdks/client-side/features/client-side-click-tracking).
```html
```
Read the [client-side click-tracking guide](/sdks/client-side/features/client-side-click-tracking) for more information.
You can **verify the installation** with the following tests:
1. Open the browser console and type in `_dubAnalytics` – if the script is installed correctly, you should see the `_dubAnalytics` object in the console.
2. Add the `?dub_id=test` query parameter to your website URL and make sure that the `dub_id` cookie is being set in your browser.
If both of these checks pass, the script is installed correctly. Otherwise, please make sure:
* The analytics script was added to the `` section of the page
* If you're using a content delivery network (CDN), make sure to purge any cached content
## Concepts
You can pass the following props to the `@dub/analytics` script to customize its behavior:
The base URL for the Dub API. This is useful for [setting up reverse
proxies](/sdks/client-side/features/reverse-proxy-support) to avoid
adblockers.
The attribution model to use for the analytics event. The following
attribution models are available:
* `first-click`: The first click model
gives all the credit to the first touchpoint in the customer journey.
* `last-click`: The last click model gives all the credit to the last
touchpoint in the customer journey.
Custom properties to pass to the cookie. Refer to
[MDN's Set-Cookie documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie) for
all available options.
Specifies the value for the `Domain` Set-Cookie attribute. This is useful
for cross-domain tracking. Example: `.example.com`
Specifies the `Date` object to be the value for the `Expires` Set-Cookie
attribute. Example: `new Date('2024-12-31')`
Specifies the number (in days) to be the value for the `Expires`
Set-Cookie attribute.
For example, to set the cookie window to 60 days (instead of the default 90 days), you can add the following to your script:
```html
```
Specifies the value for the `Path` Set-Cookie attribute. By default, the
path is considered the "default path". Example: `/`
Configure the domains that Dub will track. The following properties are available:
The Dub custom domain for [referral program client-side click tracking](http://d.to/clicks/refer) (previously `data-short-domain`).
Example: `refer.dub.co`
The Dub short domain for tracking site visits.
Example: `site.dub.co`
An array of domains for cross-domain tracking. When configured, the existing `dub_id` cookie
will be automatically appended to all outbound links targeting these domains to enable
cross-domain tracking across different applications.
Example: `"dub.sh, git.new"`
The query parameter to listen to for client-side click-tracking (e.g.
`?via=abc123`).
# React
Source: https://dub.co/docs/sdks/client-side/installation-guides/react
How to add @dub/analytics to your React or Next.js site
With Dub Analytics, you can track lead and sale conversions on your website, enabling you to measure the effectiveness of your marketing campaigns.
## Quickstart
This quick start guide will show you how to get started with Dub Analytics on your website.
Using the package manager of your choice, add the `@dub/analytics` to your project.
```bash npm
npm install @dub/analytics
```
```bash pnpm
pnpm add @dub/analytics
```
```bash yarn
yarn add @dub/analytics
```
```bash bun
bun add @dub/analytics
```
If you are using a React framework, you can use the `` component to track conversions on your website.
E.g. if you're using Next.js, you can add the `` component to your root layout component or any other pages where you want to track conversions.
```jsx app/layout.tsx
import { Analytics as DubAnalytics } from '@dub/analytics/react';
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
{children}
);
}
```
If you're using [Dub Partners](/partners/quickstart) for affiliate management, you will also need to set up the `domainsConfig.refer` property to enable [client-side click-tracking](/sdks/client-side/features/client-side-click-tracking).
```jsx app/layout.tsx
import { Analytics as DubAnalytics } from '@dub/analytics/react';
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
{children}
);
}
```
Read the [client-side click-tracking guide](/sdks/client-side/features/client-side-click-tracking) for more information.
You can **verify the installation** with the following tests:
1. Open the browser console and type in `_dubAnalytics` – if the script is installed correctly, you should see the `_dubAnalytics` object in the console.
2. Add the `?dub_id=test` query parameter to your website URL and make sure that the `dub_id` cookie is being set in your browser.
If both of these checks pass, the script is installed correctly. Otherwise, please make sure:
* The analytics script was added to the `` section of the page
* If you're using a content delivery network (CDN), make sure to purge any cached content
## Concepts
You can pass the following props to the `` component to customize its behavior:
The base URL for the Dub API. This is useful for [setting up reverse
proxies](/sdks/client-side/features/reverse-proxy-support) to avoid
adblockers.
The attribution model to use for the analytics event. The following
attribution models are available:
* `first-click`: The first click model
gives all the credit to the first touchpoint in the customer journey.
* `last-click`: The last click model gives all the credit to the last
touchpoint in the customer journey.
Specifies the value for the `Domain` Set-Cookie attribute. This is useful
for cross-domain tracking. Example: `.example.com`
Specifies the `Date` object to be the value for the `Expires` Set-Cookie
attribute. Example: `new Date('2024-12-31')`
Specifies the number (in days) to be the value for the `Expires`
Set-Cookie attribute. Example: `90`
Specifies the value for the `Path` Set-Cookie attribute. By default, the
path is considered the "default path". Example: `/`
Configure the domains that Dub will track. The following properties are available:
The Dub custom domain for [referral program client-side click tracking](http://d.to/clicks/refer)
(previously `shortDomain`).
Example: `refer.dub.co`
The Dub short domain for tracking site visits.
Example: `site.dub.co`
An array of domains for cross-domain tracking. When configured, the existing
`dub_id` cookie will be automatically appended to all outbound links
targeting these domains to enable cross-domain tracking across different
applications.
Example: `["dub.sh", "git.new"]`
The query parameter to listen to for client-side click-tracking (e.g.
`?via=abc123`).
Custom properties to pass to the script tag. Refer to
[MDN](https://developer.mozilla.org/en-US/docs/Web/API/HTMLScriptElement) for
all available options.
# Shopify
Source: https://dub.co/docs/sdks/client-side/installation-guides/shopify
How to add @dub/analytics to your Shopify store
With `@dub/analytics`, you can track lead and sale conversions on your Shopify store, enabling you to measure the effectiveness of your marketing campaigns.
You can add the `@dub/analytics` script to your Shopify store simply by installing the [Dub Shopify App](https://d.to/shopify/app) from the App Store.
Then, make sure to activate the `@dub/analytics` script by following these steps:
1. Navigate to your Shopify admin panel.
2. Go to **Online Store** > **Themes**.
3. Click on **Customize** for your current theme.
4. In the theme editor, select the **App embeds** tab.
5. Locate the **Analytics Script** for the Dub Conversions app and toggle it to activate.
You can **verify the installation** with the following tests:
1. Open the browser console and type in `_dubAnalytics` – if the script is installed correctly, you should see the `_dubAnalytics` object in the console.
2. Add the `?dub_id=test` query parameter to your website URL and make sure that the `dub_id` cookie is being set in your browser.
If both of these checks pass, the script is installed correctly. Otherwise, please make sure:
* The analytics script was added to the `` section of the page
* If you're using a content delivery network (CDN), make sure to purge any cached content
## Installation video
Here's a video showing how to install and activate the `@dub/analytics` script in your Shopify store:
# Webflow
Source: https://dub.co/docs/sdks/client-side/installation-guides/webflow
How to add @dub/analytics to your Webflow site
With `@dub/analytics`, you can track lead and sale conversions on your Webflow site, enabling you to measure the effectiveness of your marketing campaigns.
You can add the `@dub/analytics` script to your Webflow website same way you would add Google Analytics script or any other JavaScript code.
Follow these steps to add the script to your site:
* On your project's page, click on the **Webflow logo** in the left-hand side menu and choose **Project Settings**.
* Choose **[Custom Code](https://university.webflow.com/lesson/custom-code-in-the-head-and-body-tags?topics=site-settings)** from the menu and paste the Dub analytics script in the **Head Code** section.
* Click on the **Save Changes** button and then **Publish** your changes.
```html
```
If you're using [Dub Partners](/partners/quickstart) for affiliate management, you will also need to set up the `data-domains` property to enable [client-side click-tracking](/sdks/client-side/features/client-side-click-tracking).
```html
```
Read the [client-side click-tracking guide](/sdks/client-side/features/client-side-click-tracking) for more information.
You can **verify the installation** with the following tests:
1. Open the browser console and type in `_dubAnalytics` – if the script is installed correctly, you should see the `_dubAnalytics` object in the console.
2. Add the `?dub_id=test` query parameter to your website URL and make sure that the `dub_id` cookie is being set in your browser.
If both of these checks pass, the script is installed correctly. Otherwise, please make sure:
* The analytics script was added to the `` section of the page
* If you're using a content delivery network (CDN), make sure to purge any cached content
## Concepts
You can pass the following props to the `@dub/analytics` script to customize its behavior:
The base URL for the Dub API. This is useful for [setting up reverse
proxies](/sdks/client-side/features/reverse-proxy-support) to avoid
adblockers.
The attribution model to use for the analytics event. The following
attribution models are available:
* `first-click`: The first click model
gives all the credit to the first touchpoint in the customer journey.
* `last-click`: The last click model gives all the credit to the last
touchpoint in the customer journey.
Custom properties to pass to the cookie. Refer to
[MDN's Set-Cookie documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie) for
all available options.
Specifies the value for the `Domain` Set-Cookie attribute. This is useful
for cross-domain tracking. Example: `.example.com`
Specifies the `Date` object to be the value for the `Expires` Set-Cookie
attribute. Example: `new Date('2024-12-31')`
Specifies the number (in days) to be the value for the `Expires`
Set-Cookie attribute.
For example, to set the cookie window to 60 days (instead of the default 90 days), you can add the following to your script:
```html
```
Specifies the value for the `Path` Set-Cookie attribute. By default, the
path is considered the "default path". Example: `/`
Configure the domains that Dub will track. The following properties are available:
The Dub custom domain for [referral program client-side click tracking](http://d.to/clicks/refer) (previously `data-short-domain`).
Example: `refer.dub.co`
The Dub short domain for tracking site visits.
Example: `site.dub.co`
An array of domains for cross-domain tracking. When configured, the existing `dub_id` cookie
will be automatically appended to all outbound links targeting these domains to enable
cross-domain tracking across different applications.
Example: `"dub.sh, git.new"`
The query parameter to listen to for client-side click-tracking (e.g.
`?via=abc123`).
# WordPress
Source: https://dub.co/docs/sdks/client-side/installation-guides/wordpress
How to add @dub/analytics to your WordPress site
With `@dub/analytics`, you can track lead and sale conversions on your WordPress site, enabling you to measure the effectiveness of your marketing campaigns.
You can add the `@dub/analytics` script to your WordPress website same way you would add Google Analytics script or any other JavaScript code.
Follow these steps to add the script to your site:
* On your WordPress dashboard, navigate to the **Theme Editor** section under the **Appearance** menu.
* Open the **Theme Header (header.php)** file on the right column.
* Paste the Dub analytics script in the header area.
* Click on the **Update File** button to save the changes.
```html
```
If you're using [Dub Partners](/partners/quickstart) for affiliate management, you will also need to set up the `data-domains` property to enable [client-side click-tracking](/sdks/client-side/features/client-side-click-tracking).
```html
```
Read the [client-side click-tracking guide](/sdks/client-side/features/client-side-click-tracking) for more information.
You can **verify the installation** with the following tests:
1. Open the browser console and type in `_dubAnalytics` – if the script is installed correctly, you should see the `_dubAnalytics` object in the console.
2. Add the `?dub_id=test` query parameter to your website URL and make sure that the `dub_id` cookie is being set in your browser.
If both of these checks pass, the script is installed correctly. Otherwise, please make sure:
* The analytics script was added to the `` section of the page
* If you're using a content delivery network (CDN), make sure to purge any cached content
## Concepts
You can pass the following props to the `@dub/analytics` script to customize its behavior:
The base URL for the Dub API. This is useful for [setting up reverse
proxies](/sdks/client-side/features/reverse-proxy-support) to avoid
adblockers.
The attribution model to use for the analytics event. The following
attribution models are available:
* `first-click`: The first click model
gives all the credit to the first touchpoint in the customer journey.
* `last-click`: The last click model gives all the credit to the last
touchpoint in the customer journey.
Custom properties to pass to the cookie. Refer to
[MDN's Set-Cookie documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie) for
all available options.
Specifies the value for the `Domain` Set-Cookie attribute. This is useful
for cross-domain tracking. Example: `.example.com`
Specifies the `Date` object to be the value for the `Expires` Set-Cookie
attribute. Example: `new Date('2024-12-31')`
Specifies the number (in days) to be the value for the `Expires`
Set-Cookie attribute.
For example, to set the cookie window to 60 days (instead of the default 90 days), you can add the following to your script:
```html
```
Specifies the value for the `Path` Set-Cookie attribute. By default, the
path is considered the "default path". Example: `/`
Configure the domains that Dub will track. The following properties are available:
The Dub custom domain for [referral program client-side click tracking](http://d.to/clicks/refer) (previously `data-short-domain`).
Example: `refer.dub.co`
The Dub short domain for tracking site visits.
Example: `site.dub.co`
An array of domains for cross-domain tracking. When configured, the existing `dub_id` cookie
will be automatically appended to all outbound links targeting these domains to enable
cross-domain tracking across different applications.
Example: `"dub.sh, git.new"`
The query parameter to listen to for client-side click-tracking (e.g.
`?via=abc123`).
# Introduction
Source: https://dub.co/docs/sdks/client-side/introduction
Learn more about the @dub/analytics script and how to install it.
`@dub/analytics` is a lightweight (\~1kb), [open-source](https://github.com/dubinc/analytics) client-side script for [tracking conversion events](https://dub.co/help/article/dub-conversions) with Dub.
The script handles the detection of the `dub_id` query parameter and storing it as a first-party cookie, which will be used to attribute subsequent conversion events to the original link.
If you're using Dub Partners, this script also lets you [track clicks on the client-side](/sdks/client-side/features/client-side-click-tracking) using query parameters (e.g. `?via=john`). This gives you the flexibility to track clicks directly on your website or app, without needing to rely on link redirects.
## Installation guides
You can install the `@dub/analytics` script in several different ways:
Add Dub Analytics to your React app
Add Dub Analytics to your website
}
href="https://dub.co/docs/sdks/client-side/installation-guides/framer"
>
Add Dub Analytics to your Framer site
Add Dub Analytics to your Shopify store
Add Dub Analytics to your WP site
Add Dub Analytics to your Webflow site
## Features
The `@dub/analytics` script comes with the following features:
* [Conversion-tracking](/sdks/client-side/features/conversion-tracking)
* [Client-side click-tracking](/sdks/client-side/features/client-side-click-tracking)
* [Cross-domain tracking](/sdks/client-side/features/cross-domain-tracking)
* [Reverse-proxy support](/sdks/client-side/features/reverse-proxy-support)
## Properties
You can pass the following props to the `@dub/analytics` script to customize its behavior:
The base URL for the Dub API. This is useful for [setting up reverse
proxies](/sdks/client-side/features/reverse-proxy-support) to avoid
adblockers.
The attribution model to use for the analytics event. The following
attribution models are available:
* `first-click`: The first click model
gives all the credit to the first touchpoint in the customer journey.
* `last-click`: The last click model gives all the credit to the last
touchpoint in the customer journey.
Custom properties to pass to the cookie. Refer to
[MDN's Set-Cookie documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie) for
all available options.
Specifies the value for the `Domain` Set-Cookie attribute. This is useful
for cross-domain tracking. Example: `.example.com`
Specifies the `Date` object to be the value for the `Expires` Set-Cookie
attribute. Example: `new Date('2024-12-31')`
Specifies the number (in days) to be the value for the `Expires`
Set-Cookie attribute.
For example, to set the cookie window to 60 days (instead of the default 90 days), you can add the following to your script:
```html
```
Specifies the value for the `Path` Set-Cookie attribute. By default, the
path is considered the "default path". Example: `/`
Configure the domains that Dub will track. The following properties are available:
The Dub custom domain for [referral program client-side click tracking](http://d.to/clicks/refer) (previously `data-short-domain`).
Example: `refer.dub.co`
The Dub short domain for tracking site visits.
Example: `site.dub.co`
An array of domains for cross-domain tracking. When configured, the existing `dub_id` cookie
will be automatically appended to all outbound links targeting these domains to enable
cross-domain tracking across different applications.
Example: `"dub.sh, git.new"`
The query parameter to listen to for client-side click-tracking (e.g.
`?via=abc123`).
## Open-source examples
Here are some open-source code examples that you can referece:
See the full example on GitHub.
See the full example on GitHub.
# Script Variants
Source: https://dub.co/docs/sdks/client-side/variants
Learn more about the different variants of the @dub/analytics script.
Inspired by [Plausible](https://plausible.io/), our script is split into multiple variants to help you optimize your script for different use cases.
This allows us to keep the base variant of the script as lightweight as possible (\~1kb) while still allowing you to use the script in more complex use cases.
## Syntax
The base script is available on [`script.js`](https://www.dubcdn.com/analytics/script.js), and all variants are available on `script.[variant].js`.
For instance, the `outbound-domains` variant is available on [`script.outbound-domains.js`](https://www.dubcdn.com/analytics/script.outbound-domains.js).
You can also mix and match variants. For example, you can use the `site-visit` and `outbound-domains` variants together with this script: [`script.site-visit.outbound-domains.js`](https://www.dubcdn.com/analytics/script.site-visit.outbound-domains.js).
## List of variants
Here's a list of all the variants available:
* [Base Variant](#base-variant-script-js)
* [Site Visit Variant](#site-visit-variant-script-site-visit-js)
* [Outbound Domains Variant](#outbound-domains-variant-script-outbound-domains-js)
* [Combined Variant](#combined-variant)
### Base Variant (`script.js`)
The base variant of the script is the most lightweight variant of the script. It supports the following features:
* Detecting the `dub_id` query parameter and storing it as a first-party cookie.
* Tracking [client-side click events for referral programs](/sdks/client-side/features/client-side-click-tracking).
* Setting [custom cookie window](/sdks/client-side/introduction#custom-cookie-window) and [attribution models](/sdks/client-side/introduction#param-data-attribution-model)
Here's how you can use the base variant:
```typescript React
```
```html Other
```
### Site Visit Variant (`script.site-visit.js`)
`@dub/analytics` site visit feature is still in beta.
The site visit variant of the script is a variant of the script that supports tracking site visits.
On top of the features supported by the base variant, it also supports tracking the first entry page of a user, which is useful for measuring SEO and Google Ads performance.
Here's how you can use the site visit variant:
```typescript React
// the DubAnalytics component automatically detects the `domainsConfig.site` prop
// and applies the site-visit script variant for you
```
```html Other
```
### Outbound Domains Variant (`script.outbound-domains.js`)
The outbound domains variant of the script is a variant of the script that supports [cross-domain tracking](/sdks/client-side/introduction#cross-domain-tracking) across different applications.
On top of the features supported by the base variant, it also supports appending the `dub_id` cookie to all outbound links targeting the domains you configure.
Here's how you can use the outbound domains variant:
```typescript React
// the DubAnalytics component automatically detects the `domainsConfig.outbound` prop
// and applies the outbound-domains script variant for you
```
```html Other
```
### Combined Variant
You can also mix and match variants. For example, you can use the `site-visit` and `outbound-domains` variants together with this script: [`script.site-visit.outbound-domains.js`](https://www.dubcdn.com/analytics/script.site-visit.outbound-domains.js).
Here's how you can use the combined variant:
```typescript React
// the DubAnalytics component automatically detects the `domainsConfig` prop
// and applies the combined script variant for you
```
```html Other
```
## `DubAnalytics` React Component
If you're using a React application, we recommend using the `DubAnalytics` component to automatically apply the correct script variant for you.
For example, if you want to use the `outbound-domains` variant, you can do the following:
```typescript React
```
The `DubAnalytics` component will automatically detect the `domainsConfig` prop and apply the correct script variant for you.
# Analytics Embed
Source: https://dub.co/docs/sdks/embed/analytics
Learn how to embed the Dub Analytics dashboard in your application.
This feature is coming soon. If you'd like early access, please [contact
us](https://dub.co/contact/support).
# Referrals Embed
Source: https://dub.co/docs/sdks/embed/referrals
Learn how to install the Dub Referrals Embed in your application.
With [Dub Partners](/partners/quickstart), you can build a white-labeled referral dashboard that lives directly inside your app in just a few lines of code.
This way, your users can automatically enroll in your partner program **without needing to leave your app and sign up on a third-party platform**.
In this guide, we'll walk you through the steps to get started with the Dub Referrals Embed.
## Step 1: Generate embed token
First, you need to create a server API route that generates a public token, which will be used by the Dub Referrals Embed to fetch real-time conversions and earnings data from the client-side.
### Using server-side SDKs
If you're using our [server-side SDKs](/sdks/overview), you can generate an embed token using the `dub.embedTokens.referrals` method.
```ts TypeScript
const { publicToken } = await dub.embedTokens.referrals({
tenantId: user.id, // the user's ID within your application
partner: {
name: user.name, // the user's name
email: user.email, // the user's email
image: user.image, // the user's image/avatar
tenantId: user.id, // the user's ID within your application
},
});
```
```python Python
from dub import Dub
with Dub(
token="DUB_API_KEY",
) as d_client:
res = d_client.embed_tokens.referrals(request={
"tenant_id": user.id, # the user's ID within your application
"partner": {
"name": user.name, # the user's name
"email": user.email, # the user's email
"image": user.image, # the user's image/avatar
"tenant_id": user.id, # the user's ID within your application
},
})
# Handle response
print(res.public_token)
```
```go Go
package main
import(
"context"
dubgo "github.com/dubinc/dub-go"
"github.com/dubinc/dub-go/models/operations"
"log"
)
func main() {
ctx := context.Background()
s := dubgo.New(
dubgo.WithSecurity("DUB_API_KEY"),
)
res, err := s.EmbedTokens.Referrals(ctx, &operations.CreateReferralsEmbedTokenRequestBody{
TenantID: user.ID, // the user's ID within your application
Partner: &operations.Partner{
Name: user.Name, // the user's name
Email: user.Email, // the user's email
Image: user.Image, // the user's image/avatar
TenantID: user.ID, // the user's ID within your application
},
})
if err != nil {
log.Fatal(err)
}
if res != nil {
// Handle response
log.Printf("Public token: %s", res.PublicToken)
}
}
```
```php PHP
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub;
use Dub\Models\Operations;
$sdk = Dub\Dub::builder()
->setSecurity('DUB_API_KEY')
->build();
$request = new Operations\CreateReferralsEmbedTokenRequestBody(
tenantId: $user->id, // the user's ID within your application
partner: new Operations\Partner(
name: $user->name, // the user's name
email: $user->email, // the user's email
image: $user->image, // the user's image/avatar
tenantId: $user->id, // the user's ID within your application
),
);
$response = $sdk->embedTokens->referrals(
request: $request
);
if ($response->object !== null) {
// Handle response
echo $response->object->publicToken;
}
```
```ruby Ruby
require 'dub'
s = ::OpenApiSDK::Dub.new(
security: ::OpenApiSDK::Shared::Security.new(
token: "DUB_API_KEY",
),
)
req = ::OpenApiSDK::Operations::CreateReferralsEmbedTokenRequestBody.new(
tenant_id: user.id, # the user's ID within your application
partner: ::OpenApiSDK::Operations::Partner.new(
name: user.name, # the user's name
email: user.email, # the user's email
image: user.image, # the user's image/avatar
tenant_id: user.id, # the user's ID within your application
),
)
res = s.embed_tokens.referrals(req)
if !res.object.nil?
# Handle response
puts res.object.public_token
end
```
### Using REST API
If you're not using our server-side SDKs, you can generate an embed token using our REST API instead (via the [`POST /tokens/embed/referrals`](/api-reference/endpoint/create-referrals-embed-token) endpoint).
```js JavaScript
const response = await fetch("https://api.dub.co/tokens/embed/referrals", {
method: "POST",
body: JSON.stringify({
tenantId: user.id, // the user's ID within your application
partner: {
name: user.name, // the user's name
email: user.email, // the user's email
image: user.image, // the user's image/avatar
tenantId: user.id, // the user's ID within your application
},
}),
});
const data = await response.json();
const { publicToken } = data;
```
Refer to the [full API reference](/api-reference/endpoint/create-referrals-embed-token) to learn more about the properties you can pass to the `POST /tokens/embed/referrals` endpoint.
## Step 2: Install the embed
Then, with the `publicToken` from Step 1, you can install and initialize the Dub Referrals Embed. There are two ways to do this:
### React component
First, install the [NPM package](https://www.npmjs.com/package/@dub/embed-react):
```bash npm
npm install @dub/embed-react
```
```bash yarn
yarn add @dub/embed-react
```
```bash pnpm
pnpm add @dub/embed-react
```
```bash bun
bun add @dub/embed-react
```
Then use the component in your React application:
```tsx
import { useState, useEffect } from "react";
import { DubEmbed } from "@dub/embed-react";
export default function App() {
const [publicToken, setPublicToken] = useState("");
useEffect(() => {
const fetchPublicToken = async () => {
// fetching from the server API route you created in Step 1
const response = await fetch("/api/embed-token");
const data = await response.json();
setPublicToken(data.publicToken);
};
fetchPublicToken();
}, []);
if (!publicToken) {
return
Loading...
;
}
return ;
}
```
### Iframe embed
Alternatively, if you're not using React (or you're not on React `v18.2.0` or higher), you can add the iframe directly to your HTML:
```tsx
import { useState, useEffect } from "react";
export default function App() {
const [publicToken, setPublicToken] = useState("");
useEffect(() => {
const fetchPublicToken = async () => {
// fetching from the server API route you created in Step 1
const response = await fetch("/api/embed-token");
const data = await response.json();
setPublicToken(data.publicToken);
};
fetchPublicToken();
}, []);
if (!publicToken) {
return
Loading...
;
}
return (
);
}
```
## Embed options
The Dub Referrals Embed supports the following options for styling and behavior:
The type of embed to use. In this case, we're using the `referrals` type.
The theme of the embed.
Available options:
* `backgroundColor`: The background color of the embed.
Depending on the embed type, you can use the following examples to initialize the embed options:
```tsx React component
import { DubEmbed } from "@dub/embed-react";
const publicToken = "...";
;
```
```tsx iFrame embed
const publicToken = "...";
const iframeUrl = "https://app.dub.co/embed/referrals";
const iframeParams = new URLSearchParams({
token: publicToken,
theme: "light",
themeOptions: JSON.stringify({ backgroundColor: "#F5F5F5" }),
});
;
```
## Example Apps
See the full example on GitHub.
# Go SDK
Source: https://dub.co/docs/sdks/go
Learn how to integrate Dub with Go.
## Installation
```bash
go get github.com/dubinc/dub-go
```
## Basic Usage
Here's how you can use the Dub Go SDK to create a link and retrieve click analytics in timeseries format for it:
```go
package main
import (
"context"
"fmt"
"log"
"os"
dub "github.com/dubinc/dub-go"
)
func main() {
// Initialize the Dub SDK with your API key
d := dub.New(
dub.WithSecurity(os.Getenv("DUB_API_KEY")), // optional, defaults to DUB_API_KEY
)
// Create a new link
request := &operations.CreateLinkRequestBody{
URL: "https://google.com",
}
ctx := context.Background()
res, err := d.Links.Create(ctx, request)
if err != nil {
log.Fatal(err)
}
if res.LinkSchema != nil {
fmt.Println(res.LinkSchema.ShortLink) // e.g. https://dub.sh/abc123
}
// Get analytics for the link
analyticsRequest := operations.RetrieveAnalyticsRequest{
LinkId: res.LinkSchema.ID,
GroupBy: "timeseries",
Interval: "30d",
}
analyticsRes, err := d.Analytics.Retrieve(ctx, analyticsRequest)
if err != nil {
log.Fatal(err)
}
if analyticsRes.OneOf != nil {
fmt.Println(analyticsRes.OneOf) // e.g. [{ start: "2024-01-01", clicks: 100 }]
}
}
```
For more usage examples:
1. [Organizing links by external ID, tenant ID, tags, etc](/concepts/links/organization)
2. [Bulk link operations (create, update, delete)](/concepts/links/bulk-operations)
3. [Retrieving link analytics](/concepts/analytics)
You can also check out the [Go SDK quickstart](/sdks/quickstart/go) for a basic example.
## Additional Resources
Download and install the Dub Go SDK on GitHub
View the complete SDK reference documentation
Quickstart examples with the Go SDK
# Overview
Source: https://dub.co/docs/sdks/overview
Open-source client libraries for the Dub API
## Server-side SDKs
Dub offers server-side SDKs for many popular programming languages:
TypeScript library for the Dub API
Go library for the Dub API
Python library for the Dub API
Ruby library for the Dub API
PHP library for the Dub API
## Client-side SDKs
Dub analytics SDK
## Embedded Dashboards
Embed the Dub Referrals dashboard in your application
## CLI
Shorten and manage your links directly from your terminal
# PHP SDK
Source: https://dub.co/docs/sdks/php
Learn how to integrate Dub with PHP.
## Installation
```bash
composer require dub/dub-php
```
## Basic Usage
Here's how you can use the Dub PHP SDK to create a link and retrieve click analytics in timeseries format for it:
```php
setSecurity(getenv('DUB_API_KEY')) // optional, defaults to DUB_API_KEY
->build();
// Create a new link
$request = new Operations\CreateLinkRequestBody(
url: 'https://google.com',
);
try {
$response = $dub->links->create($request);
if ($response->linkSchema !== null) {
echo $response->linkSchema->shortLink; // e.g. https://dub.sh/abc123
}
// Get analytics for the link
$analyticsRequest = new Operations\RetrieveAnalyticsRequest();
$analyticsRequest->linkId = $response->linkSchema->id;
$analyticsRequest->interval = Operations\Interval::ThirtyD;
$analyticsRequest->groupBy = Operations\GroupBy::Timeseries;
$analyticsResponse = $dub->analytics->retrieve($analyticsRequest);
if ($analyticsResponse->oneOf !== null) {
print_r($analyticsResponse->oneOf); // e.g. [{ start: "2024-01-01", clicks: 100 }]
}
} catch (Throwable $e) {
// handle exception
}
```
For more usage examples:
1. [Organizing links by external ID, tenant ID, tags, etc](/concepts/links/organization)
2. [Bulk link operations (create, update, delete)](/concepts/links/bulk-operations)
3. [Retrieving link analytics](/concepts/analytics)
## Frameworks
You can use the Dub PHP SDK with any PHP framework:
1. [Usage with Laravel](/sdks/quickstart/laravel)
If you're using a different PHP framework, you can refer to the [PHP SDK quickstart](/sdks/quickstart/php) for a basic example.
## Additional Resources
Download and install the Dub PHP SDK on Packagist
View the complete SDK reference documentation
Quickstart examples with the PHP SDK
View the complete source code for the Dub PHP SDK
# Python SDK
Source: https://dub.co/docs/sdks/python
Learn how to integrate Dub with Python.
## Installation
```bash
pip install dub
```
## Basic Usage
Here's how you can use the Dub Python SDK to create a link and retrieve click analytics in timeseries format for it:
```python
import os
import dub
from dub.models import operations
# Initialize the Dub SDK with your API key
d = dub.Dub(
token=os.environ['DUB_API_KEY'], # optional, defaults to DUB_API_KEY
)
# Create a new link
res = d.links.create(request={
"url": "https://google.com",
})
print(res.short_link) # e.g. https://dub.sh/abc123
# Get analytics for the link
analytics = d.analytics.retrieve(request={
"link_id": res.id,
"interval": "30d",
"group_by": "timeseries",
})
print(analytics) # e.g. [{ "start": "2024-01-01", "clicks": 100 }]
```
For more usage examples:
1. [Organizing links by external ID, tenant ID, tags, etc](/concepts/links/organization)
2. [Bulk link operations (create, update, delete)](/concepts/links/bulk-operations)
3. [Retrieving link analytics](/concepts/analytics)
## Frameworks
You can use the Dub Python SDK with any Python framework:
1. [Usage with Flask](/sdks/quickstart/flask)
2. [Usage with Django](/sdks/quickstart/django)
If you're using a different Python framework, you can refer to the [Python SDK quickstart](/sdks/quickstart/python) for a basic example.
## Additional Resources
Download and install the Dub Python SDK on PyPI
View the complete SDK reference documentation
Quickstart examples with the Python SDK
View the complete source code for the Dub Python SDK
# Ruby SDK
Source: https://dub.co/docs/sdks/ruby
Learn how to integrate Dub with Ruby.
## Installation
```bash
gem install dub
```
## Basic Usage
Here's how you can use the Dub Ruby SDK to create a link and retrieve click analytics in timeseries format for it:
```ruby
require 'dub'
# Initialize the Dub SDK with your API key
dub = ::OpenApiSDK::Dub.new
dub.config_security(
::OpenApiSDK::Shared::Security.new(
token: ENV['DUB_API_KEY'], # optional, defaults to DUB_API_KEY
)
)
# Create a new link
req = ::OpenApiSDK::Operations::CreateLinkRequest.new(
request_body: ::OpenApiSDK::Operations::CreateLinkRequestBody.new(
url: "https://google.com"
)
)
res = dub.links.create(req)
puts res.raw_response.body # e.g. { "shortLink": "https://dub.sh/abc123" }
# Get analytics for the link
analytics_req = ::OpenApiSDK::Operations::RetrieveAnalyticsRequest.new(
link_id: res.raw_response.body["id"],
interval: ::OpenApiSDK::Operations::Interval::THIRTYD,
group_by: ::OpenApiSDK::Operations::GroupBy::TIMESERIES
)
analytics_res = dub.analytics.retrieve(analytics_req)
puts analytics_res.raw_response.body # e.g. [{ "start": "2024-01-01", "clicks": 100 }]
```
For more usage examples:
1. [Organizing links by external ID, tenant ID, tags, etc](/concepts/links/organization)
2. [Bulk link operations (create, update, delete)](/concepts/links/bulk-operations)
3. [Retrieving link analytics](/concepts/analytics)
## Frameworks
You can use the Dub Ruby SDK with any Ruby framework:
1. [Usage with Rails](/sdks/quickstart/rails)
2. [Usage with Sinatra](/sdks/quickstart/sinatra)
If you're using a different Ruby framework, you can refer to the [Ruby SDK quickstart](/sdks/quickstart/ruby) for a basic example.
## Additional Resources
Download and install the Dub Ruby SDK on RubyGems
View the complete SDK reference documentation
Quickstart examples with the Ruby SDK
View the complete source code for the Dub Ruby SDK
# TypeScript SDK
Source: https://dub.co/docs/sdks/typescript
Learn how to integrate Dub with TypeScript.
## Installation
```bash npm
npm install dub
```
```bash pnpm
pnpm add dub
```
```bash yarn
yarn add dub zod # zod is a peer dependency
```
## Basic Usage
Here's how you can use the Dub TypeScript SDK to create a link and retrieve click analytics in timeseries format for it:
```typescript
import { Dub } from "dub";
// Initialize the Dub SDK with your API key
const dub = new Dub({
token: process.env.DUB_API_KEY, // optional, defaults to DUB_API_KEY
});
// Create a new link
const link = await dub.links.create({
url: "https://google.com",
});
console.log(link.shortLink); // e.g. https://dub.sh/abc123
// Get analytics for the link
const analytics = await dub.analytics.retrieve({
link_id: link.id,
groupBy: "timeseries",
interval: "30d",
});
console.log(analytics); // e.g. [{ start: "2024-01-01", clicks: 100 }]
```
For more usage examples:
1. [Organizing links by external ID, tenant ID, tags, etc](/concepts/links/organization)
2. [Bulk link operations (create, update, delete)](/concepts/links/bulk-operations)
3. [Retrieving link analytics](/concepts/analytics)
## Frameworks
You can use the Dub TypeScript SDK with any JavaScript framework:
1. [Usage with Next.js](/sdks/quickstart/nextjs)
2. [Usage with Remix](/sdks/quickstart/remix)
3. [Usage with Nuxt](/sdks/quickstart/nuxt)
4. [Usage with Express](/sdks/quickstart/express)
If you're using a different JavaScript framework, you can refer to the [TypeScript SDK quickstart](/sdks/quickstart/typescript) for a basic example.
## Additional Resources
Download and install the Dub TypeScript SDK on NPM
View the complete SDK reference documentation
Quickstart examples with the TypeScript SDK
View the complete source code for the Dub TypeScript SDK
# Self-hosting Dub
Source: https://dub.co/docs/self-hosting
An end-to-end guide on how to self-host Dub – the open-source link attribution platform.
You can self-host Dub on your own servers and cloud infrastructure for greater control over your data and design. This guide will walk you through the entire process of setting up Dub on your own servers.
## Prerequisites
Before you begin, make sure you have the following:
* A [GitHub](https://github.com/) account
* A [Tinybird](https://www.tinybird.co/) account
* An [Upstash](https://upstash.com/) account
* A [PlanetScale](https://planetscale.com/) account
* A [Vercel](https://vercel.com/) account
* Either a [Cloudflare](https://www.cloudflare.com/) or [AWS](https://aws.com) account
You'll also need a custom domain that you will be using for your Dub instance, with an optional custom short domain for your links.
In this guide, we'll use `acme.com` as a placeholder for your custom domain, and `ac.me` as a placeholder for your custom short domain.
## Step 1: Local setup
First, you'll need to clone the Dub repo and install the dependencies.
First, clone the [Dub repo](https://d.to/github) into a public GitHub repository. If you are planning to distribute the code or allow users to interact with the code remotely (e.g., as part of a hosted application), make sure to provide source access (including modifications) as required by the [AGPLv3 license](https://d.to/license).
```bash Terminal
git clone https://github.com/dubinc/dub.git
```
Run the following command to install the dependencies:
```bash Terminal
pnpm i
```
Delete the `apps/web/vercel.json` file since cron jobs are not required for the self-hosted version:
```bash Terminal
rm apps/web/vercel.json
```
Convert the `.env.example` file to `.env`. You can start filling in the first few environment variables:
```bash Terminal
# The domain that your app will be hosted on
NEXT_PUBLIC_APP_DOMAIN=acme.com
# The short domain that your app will be using (could be the same as the above)
NEXT_PUBLIC_APP_SHORT_DOMAIN=ac.me
# The ID of the Vercel team that your app will be deployed to: https://vercel.com/docs/accounts/create-a-team#find-your-team-id
TEAM_ID_VERCEL=
# The unique access token for your Vercel account: https://vercel.com/guides/how-do-i-use-a-vercel-api-access-token
AUTH_BEARER_TOKEN=
```
You will fill in the remaining environment variables in the following steps.
## Step 2: Set up Tinybird Clickhouse database
Next, you'll need to set up the [Tinybird](https://tinybird.co) Clickhouse database. This will be used to store time-series click events data.
In your [Tinybird](https://tinybird.co/) account, create a new Workspace.
Copy your `admin` [Auth Token](https://www.tinybird.co/docs/concepts/auth-tokens.html). Paste this token as the `TINYBIRD_API_KEY` environment variable in your `.env` file.
In your newly-cloned Dub repo, navigate to the `packages/tinybird` directory.
Install the Tinybird CLI with `pip install tinybird-cli` (requires Python >= 3.8).
Run `tb auth` and paste your `admin` Auth Token.
Run `tb push` to publish the datasource and endpoints in the `packages/tinybird` directory. You should see the following output (truncated for brevity):
```bash Terminal
$ tb push
** Processing ./datasources/click_events.datasource
** Processing ./endpoints/clicks.pipe
...
** Building dependencies
** Running 'click_events'
** 'click_events' created
** Running 'device'
** => Test endpoint at https://api.us-east.tinybird.co/v0/pipes/device.json
** Token device_endpoint_read_8888 not found, creating one
** => Test endpoint with:
** $ curl https://api.us-east.tinybird.co/v0/pipes/device.json?token=p.ey...NWeaoTLM
** 'device' created
...
```
You will then need to update your [Tinybird API base URL](https://www.tinybird.co/docs/api-reference/api-reference.html#regions-and-endpoints) to match the region of your database.
From the previous step, take note of the **Test endpoint** URL. It should look something like this:
```bash Terminal
Test endpoint at https://api.us-east.tinybird.co/v0/pipes/device.json
```
Copy the base URL and paste it as the `TINYBIRD_API_URL` environment variable in your `.env` file.
```bash Terminal
TINYBIRD_API_URL=https://api.us-east.tinybird.co
```
## Step 3: Set up Upstash Redis database
Next, you'll need to set up the [Upstash](https://upstash.com) Redis database. This will be used to cache link metadata and serve link redirects.
In your [Upstash account](https://console.upstash.com/), create a new database.
For better performance & read times, we recommend setting up a global database with several read regions.

Once your database is created, copy the `UPSTASH_REDIS_REST_URL` and `UPSTASH_REDIS_REST_TOKEN` from the **REST API** section into your `.env` file.

Navigate to the [QStash tab](https://console.upstash.com/qstash) and copy the `QSTASH_TOKEN`, `QSTASH_CURRENT_SIGNING_KEY`, and `QSTASH_NEXT_SIGNING_KEY` from the **Request Builder** section into your `.env` file.

## Step 4: Set up PlanetScale MySQL database
Next, you'll need to set up a [PlanetScale](https://planetscale.com/)-compatible MySQL database. This will be used to store user data and link metadata.
{/* prettier-ignore */}
PlanetScale recently [removed their free
tier](https://planetscale.com/blog/planetscale-forever), so you'll need to pay
for this option. A cheaper alternative is to use a [MySQL database on
Railway](https://railway.app/template/mysql) (\$5/month).
For [local development](local-development), we recommend using a [local MySQL database
with PlanetScale simulator](local-development#option-1-local-mysql-database-with-planetscale-simulator-recommended) (100% free).
In your [PlanetScale account](https://app.planetscale.com/), create a new database.
Once your database is created, you'll be prompted to select your language or Framework. Select **Prisma**.

Then, you'll have to create a new password for your database. Once the password is created, scroll down to the **Add credentials to .env** section and copy the `DATABASE_URL` into your `.env` file.

In your Dub codebase, navigate to `apps/web/prisma/schema.prisma` and replace all the columns in the `DefaultDomains` model to the normalized version of your custom short domain (removing the `.` character).
For example, if your custom short domain is `ac.me`, your `DefaultDomains` model should look like this:
```prisma apps/web/prisma/schema.prisma
model DefaultDomains {
id String @id @default(cuid())
acme Boolean @default(true)
projectId String @unique
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
}
```
In the terminal, navigate to the `apps/web` directory and run the following command to generate the Prisma client:
```bash Terminal
pnpm run prisma:generate
```
Then, create the database tables with the following command:
```bash Terminal
pnpm run prisma:push
```
## Step 5: Set up GitHub OAuth
Next, [create a new GitHub App](https://github.com/settings/applications/new). This will allow you to sign in to Dub with your GitHub account.
Don't forget to set the following Callback URLs:
* `https://app.acme.com/api/auth/callback/github`
* `http://localhost:8888/api/auth/callback/github` for local development.
Optional: Set the "Email addresses" account permission to **read-only** in
order to access private email addresses on GitHub.
Once your GitHub App is created, copy the `Client ID` and `Client Secret` into your `.env` file as the `GITHUB_CLIENT_ID` and `GITHUB_CLIENT_SECRET` environment variables.
## Step 6: Set up Cloudflare R2
Dub stores user-generated assets in either S3 or S3-compatible services like [Cloudflare R2](https://cloudflare.com/r2). These include:
* Project logos
* User avatars
* [Custom Social Media Cards](https://dub.co/help/article/custom-link-previews) images
We recommend using [Cloudflare R2](https://cloudflare.com/r2) for self-hosting Dub, as it's a more cost-effective solution compared to AWS S3. Here's how you can set it up:
You'll need to subscribe to the R2 service if you haven't already.
In your [Cloudflare account](https://dash.cloudflare.com/), create a new R2 bucket. We recommend giving your bucket a descriptive name (e.g. `dubassets`) and leaving the remaining settings as is.

In your bucket settings, copy the **S3 API** value – you'll need it in Step 3.
From the R2 main page, click **Manage R2 API Tokens** on the right-hand column.

Then, click **Create API Token**.

Make sure to name your API token something relevant to the service that will be using the token.
Give it "Object Read & Write" permissions, and we recommend only applying ito to a single bucket.
You can leave the remaining settings (TTL, Client IP Address Filtering) as is, and click **Create API Token**.
After you create you token, copy the `Access Key ID` and `Secret Access Key` values – you'll need them in the next step.
Once you have your credentials, set them in your `.env` file:
```TypeScript .env
STORAGE_ACCESS_KEY_ID= // this is the Access Key ID value from Step 2
STORAGE_SECRET_ACCESS_KEY= // this is the Secret Access Key value from Step 2
STORAGE_ENDPOINT= // this is the S3 API value from Step 1
```
In order for your images to be publically accessible in R2 you need to setup a domain. You can either use your own domain or an R2.dev subdomain.
To use your own domain, you'll need to create a CNAME record in your DNS settings that points to your R2 bucket.
In you plan to use an R2.dev subdomain, make sure you "Allow Access".
Then set the `STORAGE_BASE_URL` in your `.env` file to the domain you chose.
```bash
STORAGE_BASE_URL={URL your assets as available at} # https://static.example.com
```

## Step 7: Set up Resend (optional)
Note that if you want to use magic link sign-in, this is a required step.
Next, you'll need to set up Resend for transactional emails (e.g. magic link emails):
1. Sign up for Resend and [create your API key here](https://resend.com/api-keys).
2. Copy the API key into your `.env` file as the `RESEND_API_KEY` environment variable.
3. You'll then need to set up and verify your domain by [following this guide here](https://resend.com/docs/dashboard/domains/introduction).
## Step 8: Set up Unsplash (optional)
Dub uses Unsplash's API for the [Custom Social Media Cards](https://dub.co/help/article/custom-link-previews) feature. You'll need to set up an Unsplash application to get an access key.

Check out Unsplash's [official documentation](https://unsplash.com/documentation#creating-a-developer-account) to learn how you can set up the `UNSPLASH_ACCESS_KEY` env var.
## Step 9: Deploy to Vercel
Once you've set up all of the above services, you can now deploy your app to Vercel.
If you haven't already, push up your cloned repository to GitHub by running the following commands:
```bash Terminal
git add .
git commit -m "Initial commit"
git push origin main
```
In your [Vercel account](https://vercel.com/), create a new project. Then, select your GitHub repository and click **Import**.
Make sure that your **Framework Preset** is set to **Next.js** and the **Root Directory** is set to `apps/web`.

In the **Environment Variables** section, add all of the environment variables from your `.env` file by copying all of them and pasting it into the first input field. A few notes:
* Remove the `PROJECT_ID_VERCEL` environment variable for now since we will only get the project ID after deploying the project.
* Replace the `NEXTAUTH_URL` environment variable with the app domain that you will be using (e.g. `https://app.acme.com`).
Click on **Deploy** to deploy your project.
If you get a `No Output Directory called "public" was found after the build
completed` error, make sure that your [Vercel deployment
settings](https://vercel.com/docs/deployments/configure-a-build) to make sure that they match the following:

Once the project deploys, retrieve your [Vercel project ID](https://vercel.com/docs/projects/overview#project-id) and add it as the `PROJECT_ID_VERCEL` environment variable – both in your `.env` file and in your newly created Vercel project's settings (under **Settings > Environment Variables**)
Add both the `NEXT_PUBLIC_APP_DOMAIN` and `NEXT_PUBLIC_APP_SHORT_DOMAIN` as domains in your Vercel project's settings (under **Settings** > **Domains**). You can follow this guide to learn [how to set up a custom domain on Vercel](https://vercel.com/docs/projects/domains/add-a-domain).
Go back to the **Deployments** page and redeploy your project.
Once the deployment is complete, you should be able to visit your app domain (e.g. `https://app.acme.com`) and see the following login page:

## Caveats
This guide is meant to be a starting point for self-hosting Dub. It currently depends on the following services to work:
* [Tinybird](https://www.tinybird.co/) for the analytics database
* [Upstash](https://upstash.com/) for the Redis database
* [PlanetScale](https://planetscale.com/) for the MySQL database
* [Vercel](https://vercel.com/) for hosting & [Edge Middleware](https://vercel.com/docs/functions/edge-middleware)
In the future, we plan to make it easier to self-host Dub by making these dependencies optional by swapping them out for native databases (e.g. mysql, redis, clickhouse, [GeoLite2](https://github.com/GitSquared/node-geolite2-redist) etc.)
Also, Docker is currently not supported, but we have a few [open](https://github.com/dubinc/dub/issues/25) [issues](https://github.com/dubinc/dub/issues/378) and [PRs](https://github.com/dubinc/dub/pull/391) for it.