# Bulk create links
post /links/bulk
Bulk create up to 100 links for the authenticated workspace.
Bulk link creation does not support the following features: [Link
cloaking](https://dub.co/help/article/link-cloaking), [Custom social media
cards](https://dub.co/help/article/custom-social-media-cards). Also, [webhook
events](/integrations/webhooks) will not be triggered when using this
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](/integrations/webhooks) will not be triggered when using this
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](/integrations/webhooks) will not be triggered when using this
endpoint.
# Create a customer
post /customers
Create a customer for the authenticated workspace.
# Create a domain
post /domains
Create a domain for the authenticated workspace.
# Create a new link
post /links
Create a new link for the authenticated workspace.
# Create a new tag
post /tags
Create a new tag for the authenticated workspace.
# Delete a customer
delete /customers/{id}
Delete a customer from a workspace.
# 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 link
delete /links/{linkId}
Delete a link for the authenticated workspace.
# Retrieve a customer
get /customers/{id}
Retrieve a customer by ID for the authenticated workspace.
# 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`.
# Retrieve a list of customers
get /customers
Retrieve a list of customers for the authenticated workspace.
# Retrieve a list of domains
get /domains
Retrieve a list of domains associated with the authenticated workspace.
# Retrieve a list of events
get /events
Retrieve a paginated list of events for the authenticated workspace.
Events endpoints require a [Business plan](https://d.to/business) subscription
or higher.
# Retrieve a list of links
get /links
Retrieve a paginated list of links for the authenticated workspace.
# Retrieve a list of tags
get /tags
Retrieve a list of tags for the authenticated workspace.
# Retrieve a QR code
get /qr
Retrieve a QR code for a link.
# Retrieve a workspace
get /workspaces/{idOrSlug}
Retrieve a workspace for the authenticated user.
# 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://d.to/pro) subscription or
higher.
# Retrieve links count
get /links/count
Retrieve the number of links for the authenticated workspace. The provided query parameters allow filtering the returned links.
# Track a lead
post /track/lead
Track a lead for a short link.
# Track a sale
post /track/sale
Track a sale for a short link.
# Update a customer
patch /customers/{id}
Update a customer for the authenticated workspace.
# Update a domain
patch /domains/{slug}
Update a domain for the authenticated workspace.
# 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
patch /tags/{id}
Update a tag in the workspace.
# Update a workspace
patch /workspaces/{idOrSlug}
Update a workspace by ID or slug.
# 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.
# Errors
Troubleshoot problems with this comprehensive breakdown of all error codes.
Dub.co 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"
}
}
```
## Error Codes
Here is a list of all error codes Dub.co API returns:
### `bad_request`
* **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.
### `unauthorized`
* **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.
### `forbidden`
* **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.
### `not_found`
* **Status:** 404
* **Problem:** The server has not found anything matching the request URI.
* **Solution:** Check the request and make sure the resource exists.
### `conflict`
* **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.
### `invite_expired`
* **Status:** 410
* **Problem:** The invite has expired.
* **Solution:** Generate a new invite.
### `unprocessable_entity`
* **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.
### `rate_limit_exceeded`
* **Status:** 429
* **Problem:** The request has been rate limited.
* **Solution:** Wait for a while and try again.
### `internal_server_error`
* **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.
# Introduction
Fundamental concepts of Dub.co's API.
Dub.co's API is now generally available. [Read the
announcement](https://dub.co/blog/announcing-dub-api).
## Base URL
Dub.co'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.co'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
```
Learn more about [how to get your API key](/api-reference/tokens).
## Response Codes
The API returns standard HTTP response codes to indicate the success or failure of an API request. Here are a few examples:
| Code | Description |
| ----- | ---------------------------------------------------------------------------------------------- |
| `200` | The request was successful. |
| `400` | The request was invalid or cannot be served. |
| `401` | The request requires user authentication. |
| `403` | The server understood the request, but refuses to authorize it. |
| `404` | The requested resource could not be found. |
| `429` | Too many requests. |
| `500` | The server encountered an unexpected condition which prevented it from fulfilling the request. |
## Caveats
* Dub's API is currently in public beta and is subject to change. However, we will do our best to keep breaking changes to a minimum.
# Pagination
Learn how to paginate through resources in the API.
The pagination feature allows you to retrieve a subset of resources from the API. 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.
## 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 '
```
```typescript TypeScript
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
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.co plan, you can expect the following rate limits:
| Plan | Rate limit |
| ----------------------------------------------------- | -------------------------------------------------------------------- |
| Free | 60 requests per minute |
| [Pro](https://dub.co/help/article/pro-plan) | 600 requests per minute |
| [Business](https://dub.co/help/article/business-plan) | 3,000 requests per minute |
| [Enterprise](https://dub.co/enterprise) | Custom – [reach out to sales](https://dub.co/enterprise) 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)
```typescript TypeScript
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.
```typescript TypeScript
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
```
Alternatively, you can also periodically fetch a [list of links](/api-reference/endpoint/retrieve-a-list-of-links) sorted by last clicked, and [paginate](/api-reference/pagination) through the links until you get the one that has the same `clicks` value as the one you have stored – that way you know you've updated all the links that were clicked on since your last update.
```typescript TypeScript
await dub.links.list({
sort: "lastClicked",
page: 1,
});
```
```python Python
res = d.links.list(request={
"sort": "lastClicked",
"page": 1,
})
```
```go Go
func main() {
// Retrieve the links sorted by last clicked
request := operations.ListLinksRequest{
Sort: "lastClicked",
Page: 1,
}
ctx := context.Background()
res, err := d.Links.List(ctx, request)
if err != nil {
log.Fatal(err)
}
if res.OneOf != nil {
// handle response
}
}
```
```ruby Ruby
req = ::OpenApiSDK::Operations::ListLinksRequest.new(
sort: ::OpenApiSDK::Operations::Sort::LAST_CLICKED,
page: 1,
)
res = dub.links.list(req)
puts res.raw_response.body
```
# API Keys
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.
## Secret and publishable keys
There are two types of API keys on Dub – **Secret keys** and **Publishable keys**.
Here are the key differences between them:
| Type | Format | When to use |
| :-------------- | :------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Secret** | `dub_xxxxxxxx` | Use this key to authenticate requests on your server. 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. |
| **Publishable** | `dub_publishable_xxxxxxxx` | Use this key to authenticate requests on the client side for operations like [client-side click tracking](/conversions/clicks/introduction#client-side-click-tracking). |
## Create a secret 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.
When making API calls, if your API Key have insufficient permissions, the error should tell you which permissions you need.
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.
## Create a publishable API key
To create a publishable key, you can go to **Settings** > [**API Keys**](https://app.dub.co/settings/tokens) and click on **Create publishable key**. The key will be in the format `dub_publishable_xxxxxxxx`.
Publishable keys are currently in beta. If you'd like access, [reach out to us
via
email](mailto:support@dub.co?subject=I%20want%20access%20to%20publishable%20keys).
## 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.
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.
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.
Machine users are available exclusively to workspace owners. If you are not
the owner of the workspace, this option will be disabled when creating an API
key.
# Introduction
Click-tracking with Dub Conversions
Clicks are the starting point for all conversion events on Dub.
A click event can be triggered by:
* A user clicking on a [Dub link](https://dub.co/help/article/how-to-create-link)
* A user scanning a [Dub QR code](https://dub.co/features/qr-codes)
In [Dub Analytics](https://dub.co/help/article/dub-analytics), you can [filter between QR code scans and link clicks](https://dub.co/help/article/filter-analytics-by-trigger), giving you a full picture of how your various marketing channels are performing.
Clicks are tracked automatically for all Dub short links upon a redirect event – and you also have the flexibility to track clicks via query parameters using our [Client-side SDK](/sdks/client-side/introduction).
## 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.
## Client-side click-tracking
Alternatively, you can track clicks on the client-side using query parameters (e.g. `?via=john`, `?ref=jane`). This gives you the flexibility to track clicks directly on your website or app, without needing to rely on link redirects.
A few use cases include:
* You are migrating from an existing affiliate management platform that uses query parameters to track conversions and don't want ask your affiliates to update all their links
* You need to use an unfurled URL for the platform you're sharing the link on (no short links allowed)
* You have dynamically generated referral pages (e.g. [Tesla](https://www.tesla.com/referral/peeroke520149)) and want to track clicks using a `track()` function inside your application code.
With our [Client-side SDK](/sdks/client-side/introduction), you can do exactly that.
Here's how you can enable client-side click-tracking for your links:
### Step 1: Add a unique `identifier` to your links
First, you need to add a unique `identifier` to your links. This `identifier` is unique across your workspace and will be used to identify the corresponding link when passed as the query parameter (e.g. `?via=jane`).
There are two ways to add an `identifier` to your links:
1. Inside the Dub Link Builder, click on the **More options** button and select **Advanced Settings**. You can also use the `A` keyboard shortcut.
2. Under the **Identifier** section, enter a unique value that corresponds to the query parameter you want to use for the click-tracking.
3. Click on the **Save** button to save the changes.
If you're using the [Dub API](/api-reference/introduction) to programmatically create your links, you can use the `identifier` field to specify the unique identifier for the link.
Here's an example of how to create a link with a custom `identifier`:
```typescript TypeScript
await dub.links.create({
url: "https://google.com",
identifier: "my-custom-identifier",
});
```
```python Python
res = d.links.create(request={
url: "https://google.com",
identifier: "my-custom-identifier",
});
```
```go Go
func main() {
s := dubgo.New(
dubgo.WithSecurity("DUB_API_KEY"),
)
ctx := context.Background()
res, err := s.Links.Create(ctx, &operations.CreateLinkRequestBody{
URL: "https://google.com",
Identifier: "my-custom-identifier",
})
if err != nil {
log.Fatal(err)
}
if res != nil {
// handle response
}
}
```
```ruby Ruby
req = ::OpenApiSDK::Operations::CreateLinkRequestBody.new(
url: "https://google.com",
identifier: "my-custom-identifier",
)
res = s.links.create(req)
```
### Step 2: Create a Publishable Key
Next, you'll need to create a Publishable Key that you'll use to initialize the [Client-side SDK](/sdks/client-side/introduction). The key has the following format:
```
dub_publishable_xxxxxxxxxxxxxxxxxxxxxxxx
```
You can create a Publishable Key in the [**Tokens** settings page](https://app.dub.co/settings/tokens) in your Dub workspace.
### Step 3: Install the Client-side SDK
Lastly, you'll need to install the [Client-side SDK](/sdks/client-side/introduction) and initialize it with the Publishable Key you created 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 `` component:
The base URL for the Dub API. This is useful for setting up reverse proxies to
avoid adblockers.
The publishable API key to use for client-side click tracking. Get your
publishable API key from your [Dub workspace's token settings
page](https://app.dub.co/settings/tokens).
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: `/`
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.
### Step 4 (Optional, but recommended): Set up 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/click",
destination: "https://api.dub.co/track/click",
},
];
},
};
```
```json Vercel
// vercel.json
{
"rewrites": [
{
"source": "/_proxy/dub/track/click",
"destination": "https://api.dub.co/track/click"
}
]
}
```
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
```
### Step 5: Verify your setup
To verify that your click-tracking is working, run your website locally with the specific query parameter (e.g. `?via=jane`) and check if the following is true:
1. The click tracked correctly in the [**Analytics**](https://app.dub.co/analytics) tab of your Dub workspace.
2. There is a successful `/track/click` request in your browser's **Network** tab (and no errors in the **Console** tab).
3. The `dub_id` cookie is being set in your browser upon a successful click-tracking request.
# Auth0
Learn how to track a lead conversion event with Auth0 and the Dub SDK
When it comes to conversion tracking, 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. [Install the @dub/analytics client-side SDK](/conversions/quickstart#step-1-install-the-dub-analytics-client-side-sdk)
2. [Install the Dub server-side SDK](/conversions/quickstart#step-2-install-the-dub-server-side-sdk)
3. [Enable conversion tracking for your links](/conversions/quickstart#step-4-enable-conversion-tracking-for-your-links)
## Configure Auth0
Next, configure Auth0 to track a lead conversion event.
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",
customerId: 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 | Description | Required |
| :--------------- | :----------------------------------------------------------------------------------------------------------------------- | :------- |
| `clickId` | The unique `dub_id` parameter that the lead conversion event is attributed to. | Yes |
| `eventName` | The name of the event. Example: "Sign up". | Yes |
| `customerId` | The unique ID of the customer in your system. Will be used to identify and attribute all future events to this customer. | Yes |
| `customerEmail` | The email address of the customer. If not passed, a random email address will be generated. | No |
| `customerName` | The name of the customer. If not passed, a random name will be generated (e.g. "Big Red Caribou"). | No |
| `customerAvatar` | The avatar URL of the customer. If not passed, a random avatar URL will be generated. | No |
## View your conversions
Once you've set this up, all your tracked lead events will show up on your [Analytics dashboard](https://dub.co/help/article/dub-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 of leads over time.
* **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 lead event that occurs across all your links in your workspace.
## Example Apps
See the full example on GitHub.
# Clerk
Learn how to track a lead conversion event with Clerk and the Dub SDK
When it comes to conversion tracking, 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. [Install the @dub/analytics client-side SDK](/conversions/quickstart#step-1-install-the-dub-analytics-client-side-sdk)
2. [Install the Dub server-side SDK](/conversions/quickstart#step-2-install-the-dub-server-side-sdk)
3. [Enable conversion tracking for your links](/conversions/quickstart#step-4-enable-conversion-tracking-for-your-links)
## Configure Clerk
We're working on this guide. Check back soon!
## Example Apps
See the full example on GitHub.
# NextAuth.js
Learn how to track a lead conversion event with NextAuth.js and the Dub SDK
When it comes to conversion tracking, 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. [Install the @dub/analytics client-side SDK](/conversions/quickstart#step-1-install-the-dub-analytics-client-side-sdk)
2. [Install the Dub server-side SDK](/conversions/quickstart#step-2-install-the-dub-server-side-sdk)
3. [Enable conversion tracking for your links](/conversions/quickstart#step-4-enable-conversion-tracking-for-your-links)
## Configure NextAuth.js Options
Then, set up your NextAuth.js configuration options to track a lead conversion event 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) {
// check if dub_id cookie is present
const dub_id = cookies().get("dub_id")?.value;
if (dub_id) {
// send lead event to Dub
await dub.track.lead({
clickId: dub_id,
eventName: "Sign Up",
customerId: user.id,
customerName: user.name,
customerEmail: user.email,
customerAvatar: user.image,
});
// delete the dub_id cookie
cookies().set("dub_id", "", {
expires: new Date(0),
});
}
}
},
},
};
```
```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",
customerId: user.id,
customerName: user.name,
customerEmail: user.email,
customerAvatar: user.image,
});
}
}
},
},
});
```
Here's the full list of attributes you can pass when sending a lead event:
| Property | Description | Required |
| :--------------- | :----------------------------------------------------------------------------------------------------------------------- | :------- |
| `clickId` | The unique `dub_id` parameter that the lead conversion event is attributed to. | Yes |
| `eventName` | The name of the event. Example: "Sign up". | Yes |
| `customerId` | The unique ID of the customer in your system. Will be used to identify and attribute all future events to this customer. | Yes |
| `customerEmail` | The email address of the customer. If not passed, a random email address will be generated. | No |
| `customerName` | The name of the customer. If not passed, a random name will be generated (e.g. "Big Red Caribou"). | No |
| `customerAvatar` | The avatar URL of the customer. If not passed, a random avatar URL will be generated. | No |
## 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 set this up, all your tracked lead events will show up on your [Analytics dashboard](https://dub.co/help/article/dub-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 of leads over time.
* **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 lead event that occurs across all your links in your workspace.
## Example Apps
See the full example on GitHub.
# Supabase Auth
Learn how to track a lead conversion event with Supabase Auth and the Dub SDK
When it comes to conversion tracking, 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 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. [Install the @dub/analytics client-side SDK](/conversions/quickstart#step-1-install-the-dub-analytics-client-side-sdk)
2. [Install the Dub server-side SDK](/conversions/quickstart#step-2-install-the-dub-server-side-sdk)
3. [Enable conversion tracking for your links](/conversions/quickstart#step-4-enable-conversion-tracking-for-your-links)
## Configure Supabase Auth
Next, configure Supabase Auth to track a lead conversion event 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",
customerId: 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",
customerId: 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 | Description | Required |
| :--------------- | :----------------------------------------------------------------------------------------------------------------------- | :------- |
| `clickId` | The unique `dub_id` parameter that the lead conversion event is attributed to. | Yes |
| `eventName` | The name of the event. Example: "Sign up". | Yes |
| `customerId` | The unique ID of the customer in your system. Will be used to identify and attribute all future events to this customer. | Yes |
| `customerEmail` | The email address of the customer. If not passed, a random email address will be generated. | No |
| `customerName` | The name of the customer. If not passed, a random name will be generated (e.g. "Big Red Caribou"). | No |
| `customerAvatar` | The avatar URL of the customer. If not passed, a random avatar URL will be generated. | No |
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 set this up, all your tracked lead events will show up on your [Analytics dashboard](https://dub.co/help/article/dub-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 of leads over time.
* **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 lead event that occurs across all your links in your workspace.
## Example Apps
See the full example on GitHub.
# Quickstart
Learn how to get started with Dub Conversions
Dub Conversions is currently in closed beta. If you'd like to join the beta,
[reach out to us via
email](mailto:support@dub.co?subject=I%20want%20to%20join%20the%20Dub%20Conversions%20beta).
[Dub Conversions](https://dub.co/help/article/dub-conversions) is a powerful tool that lets you to 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.
Learn more about [how Dub Conversions works](https://dub.co/help/article/dub-conversions).
In this guide, we'll walk you through the steps to get started with Dub Conversions.
## Step 1: Install the Dub analytics client-side SDK
First step is to install the [Dub analytics client-side SDK](/sdks/client-side/introduction).
This will handle 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.
Depending on which framework or platform you're using, you can install the script in different ways:
Add Dub Analytics to your React or Next.js app.
Add Dub Analytics to websites built using Webflow, WordPress, or Framer.
## Step 2: Install the Dub server-side SDK
Dub Conversions uses [server-side event tracking](https://dub.co/help/article/dub-conversions#how-are-conversions-tracked) to track conversions. To enable this, you'll need to install the Dub server-side SDKs.
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:
* [Tracking a lead event](/api-reference/endpoint/track-lead)
* [Tracking a sale event](/api-reference/endpoint/track-sale)
## Step 3: Start sending events
Now that you've installed the SDKs, you can start sending events to Dub. Since click events are tracked automatically, you'll only need to send events for the following cases:
* [Lead events](#step-3a-sending-lead-events)
* [Sale events](#step-3b-sending-sale-events)
### Step 3a: Sending Lead Events
The first event you'll want to send 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:
Tracking a lead conversion event with NextAuth.js and the Dub SDK
Tracking a lead conversion event with Clerk and the Dub SDK
Tracking a lead conversion event with Supabase Auth and the Dub SDK
Tracking a lead conversion event with Auth0 and the Dub SDK
When sending a lead conversion event, you can include the following properties:
| Property | Description | Required |
| :--------------- | :----------------------------------------------------------------------------------------------------------------------- | :------- |
| `clickId` | The unique `dub_id` parameter that the lead conversion event is attributed to. | Yes |
| `eventName` | The name of the event. Example: "Sign up". | Yes |
| `customerId` | The unique ID of the customer in your system. Will be used to identify and attribute all future events to this customer. | Yes |
| `customerEmail` | The email address of the customer. If not passed, a random email address will be generated. | No |
| `customerName` | The name of the customer. If not passed, a random name will be generated (e.g. "Big Red Caribou"). | No |
| `customerAvatar` | The avatar URL of the customer. If not passed, a random avatar URL will be generated. | No |
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.
### Step 3b: Sending 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:
Tracking a sale conversion event with Stripe and the Dub SDK
Tracking a sale conversion event with Shopify and the Dub SDK
Alternatively, you can also send sale events manually using [our SDKs](/sdks/overview) or the [`POST /track/sale` API endpoint](/api-reference/endpoint/track-sale).
Here are the properties you can include when sending a sale event:
| Property | Description | Required |
| :----------------- | :---------------------------------------------------------- | :------- |
| `customerId` | The unique ID of the customer in your system. | Yes |
| `amount` | The amount of the sale in cents. | Yes |
| `paymentProcessor` | The name of the payment processor that processed the sale. | Yes |
| `eventName` | The name of the event. Defaults to "Purchase". | No |
| `invoiceId` | The unique ID of the invoice in your system. | No |
| `currency` | The currency of the sale. Defaults to "usd". | No |
| `metadata` | An object containing additional information about the sale. | No |
## Step 4: Enable conversion tracking for your links
Lastly, you'll need enable conversion tracking for your Dub links for to be able to start tracking conversions. You can do this by:
1. Go to your [Dub dashboard](https://app.dub.co)
2. Click on the short link you want to track conversions for, which will open up the [Link Builder](https://dub.co/help/article/dub-link-builder).
3. Enable **Conversion Tracking** and click **Save changes**
Alternatively, you can also enable conversion tracking programmatically via the [Dub API](/api-reference). All you need to do is pass `trackConversion: true` when creating or updating a link:
```typescript TypeScript
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 5: View your conversions
Once you've enabled conversion tracking for your links, all your tracked conversions will show up on your [Analytics dashboard](https://dub.co/help/article/dub-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.
# Shopify
Learn how to track a sale conversion event with Shopify
We're working on this guide. Check back soon!
# Stripe
Learn how to track a sale conversion event with Stripe
When it comes to conversion tracking, 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
In this guide, we will be focusing on tracking sale events with Stripe as the payment processor by leveraging Dub's Stripe integration.
## 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. [Install the @dub/analytics client-side SDK](/conversions/quickstart#step-1-install-the-dub-analytics-client-side-sdk)
2. [Install the Dub server-side SDK](/conversions/quickstart#step-2-install-the-dub-server-side-sdk)
3. [Enable conversion tracking for your links](/conversions/quickstart#step-4-enable-conversion-tracking-for-your-links)
Then, depending on your authentication provider, follow the relevant guide to set up lead conversion tracking:
## Step 1: Enable the Stripe Integration on Dub
Navigate to the [Stripe integration page](https://app.dub.co/integrations/stripe) in your Dub workspace.
Click on the "Enable" button to enable the Stripe integration, which will redirect you to the Stripe App installation flow.
Select the Stripe account to install the app on, and select "Install App".
Once this is done, you will be redirected back to your Dub workspace and you
should see that the integration is now installed.
Navigate to your [Stripe "Installed Apps" dashboard](https://dashboard.stripe.com/settings/apps/dub.co) and verify that the Dub app is installed. We also recommmend installing the app in test mode to be able to test your end-to-end flow without using real money.
Dub's Stripe integration will automatically forward the following events to Dub:
* `customer.created`: When a new customer is created
* `checkout.session.completed`: When a customer completes a checkout session
* `invoice.paid`: When an invoice is paid (for tracking recurring subscriptions)
## Step 2: Associate Stripe customer with your customer ID
Next, we'll need to associate the [Stripe customer object](https://docs.stripe.com/api/customers/object) with the user ID in your database (which we tracked in the [lead conversion tracking step](/conversions/quickstart#step-3a-sending-lead-events)).
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), we passed the user's unique user ID along with the click event ID in the `dub.track.lead` call?
```typescript
await dub.track.lead({
clickId,
eventName: "Sign Up",
customerId: user.id,
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.
There are 2 ways to associate the Stripe customer object with the user ID in your database:
1. [Include your customer's unique user ID in checkout sessions](#option-1-include-your-customers-unique-user-id-in-checkout-sessions)
2. [Pass the user ID and the click event ID in the Stripe customer creation flow](#option-2-pass-the-user-id-and-the-click-event-id-in-the-stripe-customer-creation-flow)
### Option 1: Include your customer's unique user ID in checkout sessions
When you [create checkout sessions](https://docs.stripe.com/api/checkout/sessions/create) for your users, pass your customer's unique user ID in your database as the `dubCustomerId` value in the `metadata` field.
```typescript app/api/upgrade/route.ts
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
},
});
```
### Option 2: Pass the user ID and the click event ID in the Stripe customer creation flow
Alternatively, if you don't use Stripe's checkout session creation flow, 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):
```typescript app/api/create-customer/route.ts
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,
},
});
```
## 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.
## Example Apps
See the full example on GitHub.
# Data model
A quick overview of how Dub is structured.
Whether you are using Dub's [API](/api-reference/introduction) or not, this page is a quick way to understand how Dub works.
Within Dub, all data belongs to a [**Workspace**](#workspace). Within a workspace, you have [Links](#links), [Domains](#domains), [Tags](#tags), and more.
## Workspace
[Workspaces](https://dub.co/help/article/what-is-a-workspace) is the defacto way of organizing your links and working with your team on Dub. You can think of a Dub workspace like a workspace on Slack or Discord – it's a shared space where you can [invite your team members](https://dub.co/help/article/how-to-invite-teammates) to collaborate on links.
When interacting with Dub's API, you'll also need to create a [workspace API key](/api-reference/tokens) to authenticate your requests.
The unique ID of the workspace.
The name of the workspace.
The slug of the workspace.
The logo of the workspace.
The usage of the workspace.
The usage limit of the workspace.
The links usage of the workspace.
The links limit of the workspace.
The domains limit of the workspace.
The tags limit of the workspace.
The users limit of the workspace.
The plan of the workspace.
The Stripe ID of the workspace.
The date and time when the billing cycle starts for the workspace.
The date and time when the workspace was created.
The role of the authenticated user in the workspace.
The role of the authenticated user in the workspace.
The domains of the workspace.
The domain of the workspace.
Indicates if the domain is the primary domain.
## Links
Links are the bread and butter of Dub. You can shorten any URL to a Dub link, which you can then share with your audience. Links can be [created](/api-reference/endpoint/create-a-new-link), [updated](/api-reference/endpoint/edit-a-link), and [deleted](/api-reference/endpoint/delete-a-link) via the [Dub API](/api-reference/introduction) or the [Dub dashboard](https://app.dub.co).
The unique ID of the short link.
The domain of the short link. If not provided, the primary domain for the
workspace will be used (or dub.sh if the workspace has no domains).
The short link slug. If not provided, a random 7-character slug will be
generated.
This is the ID of the link in your database. If set, it can be used to
identify the link in the future. Must be prefixed with `ext_` when provided to
`links.get`, `links.update`, and `links.delete` methods.
The destination URL of the short link.
Whether the short link is archived.
The date and time when the short link will expire in ISO-8601 format.
The URL to redirect to when the short link has expired.
The password required to access the destination URL of the short link.
Whether the short link uses Custom Social Media Cards feature.
The title of the short link generated via api.dub.co/metatags. Will be used
for Custom Social Media Cards if proxy is true.
The description of the short link generated via api.dub.co/metatags. Will be
used for Custom Social Media Cards if proxy is true.
The image of the short link generated via api.dub.co/metatags. Will be used
for Custom Social Media Cards if proxy is true.
Whether the short link uses link cloaking.
The iOS destination URL for the short link for iOS device targeting.
The Android destination URL for the short link for Android device targeting.
Whether the short link's stats are publicly accessible.
The unique ID of the tag assigned to the short link. This field is deprecated
– use `tags` instead.
The tags assigned to the short link.
The unique ID of the tag.
The name of the tag.
The color of the tag.
The comments for the short link.
The full URL of the short link, including the https protocol (e.g.
`https://dub.sh/try`).
The full URL of the QR code for the short link (e.g.
`https://api.dub.co/qr?url=https://dub.sh/try`).
The UTM source of the short link.
The UTM medium of the short link.
The UTM campaign of the short link.
The UTM term of the short link.
The UTM content of the short link.
The user ID of the creator of the short link.
The workspace ID of the short link.
The number of clicks on the short link.
The date and time when the short link was last clicked.
The date and time when the short link was created.
The date and time when the short link was last updated.
The project ID of the short link. This field is deprecated – use `workspaceId`
instead.
## Domains
On Dub, you can [add custom domains](https://dub.co/help/article/how-to-add-custom-domain) to create branded short links for better brand recognition. You can also [set a primary domain](https://dub.co/help/article/how-to-set-primary-domain) for it to be used as the default domain for new links (both via the API and the dashboard).
The unique identifier of the domain.
The domain name.
Whether the domain is verified.
Whether the domain is the primary domain for the workspace.
Whether the domain is archived.
Provide context to your teammates in the link creation modal by showing them
an example of a link to be shortened.
The URL to redirect to when a link under this domain has expired.
The page your users will get redirected to when they visit your domain.
The type of redirect to use for this domain. Available values are `redirect`
and `rewrite`.
The number of clicks on the domain.
## Tags
Tags are a way to organize your links. You can [add tags to your links](https://dub.co/help/article/how-to-use-tags) to categorize them and make them easier to find. You can also [filter analytics by tags](https://dub.co/blog/introducing-tags#filtering-analytics-by-tags) to get a better understanding of how your campaigns are performing.
The unique ID of the tag.
The name of the tag.
The color of the tag.
# Examples
Explore ideas and examples of what you can build with the Dub API
## Dub Links
With Dub's [API](/api-reference), you can integrate Dub's link infrastructure into your application. This includes use cases like:
* Programmatically creating short links
* Using OAuth 2.0 to authenticate with Dub
With Dub's powerful [Analytics API](/api-reference/endpoint/retrieve-analytics), you can access real-time analytics data for your links. This allows you to build user-facing analytics dashboards, a la the ones on [OSS Gallery](https://oss.gallery/projects/dub).
Mini link shortener built with the Dub Typescript SDK
Example app that shows how to use OAuth 2.0 to authenticate with Dub
Programmatically create short links on-demand using Dub's TypeScript SDK
Create code snippets, browse AI prompts, create extension icons and more.
## Dub Conversions
With [Dub Conversions](/conversions/quickstart), you can build white-labeled referral dashboards that live directly in your application (leveraging our [Analytics API](/api-reference/endpoint/retrieve-analytics) as well).
You can also combine Dub Conversions with [Webhooks](/integrations/webhooks) to build custom referral workflows:
* give both the referrer and referee 1 month free of your product
* send a swag link when a user reaches 5 referrals
Dub Conversions is currently in closed beta. If you'd like to join the beta,
[reach out to us via
email](mailto:support@dub.co?subject=I%20want%20to%20join%20the%20Dub%20Conversions%20beta).
Example app that shows how to use Dub Conversions with the Dub Typescript
SDK
# Overview
Integrate Dub.co 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.
Below are some of the apps that integrate with Dub:
## Official integrations
}
href="https://d.to/ray"
>
Shorten links with Raycast
}
href="https://d.to/zapier"
>
Shorten and manage your links with Zapier
}
href="https://d.to/make"
>
Shorten links with Make.com
}
href="https://d.to/slack"
>
Shorten your links directly in Slack
## Unofficial integrations
}
href="https://d.to/dropshare"
>
Shorten your Dropshare links
}
href="https://d.to/shortshare"
>
Use your Dub.co links on Shortshare
Bubble
}
href="https://d.to/bubble"
>
Shorten links with Bubble
}
href="https://d.to/pipedream"
>
Shorten links with Pipedream
## Building your own integrations
You can build your own integrations with Dub.co's link infrastructure using our [API](/api-reference/introduction).
1. Read the documentation on how to [create links](/api-reference/endpoint/create-a-new-link).
2. Learn how to [integrate Dub into your application](/integrations/quickstart).
3. [Reach out to us](mailto:support@dub.co?subject=New%20Dub.co%20Integration) to feature your integration in the integrations marketplace.
## Upcoming integrations
Here are some services we're working on integrating with Dub.co:
WordPressChrome Extension
## No-code Resources
If you're looking to integrate with Dub using no-code tools like Make.com and Smartsuite, here are some resources to help you get started:
# Integrate with Dub
Learn how to set up Dub OAuth applications to authenticate users with OAuth 2.0.
Integrations allow you to extend the capabilities of Dub and seamlessly connect with third-party platforms and services.
In this guide, you will learn how to create and manage integrations on Dub, allowing you to incorporate Dub's link management infrastructure 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
```
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](http://localhost:3334/sdks/overview) with the access token.
Here is an example of how you can create a link using the [Dub TypeScript SDK](http://localhost:3334/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. 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).
# Webhooks
Use webhooks to build custom integrations with Dub.
Dub Webhooks are currently in closed beta and would require a [Business
plan](https://dub.co/help/article/business-plan) subscription and above. If
you'd like to join the beta, [reach out to us via
email](mailto:support@dub.co?subject=I%20want%20to%20join%20the%20Dub%20Webhooks%20beta).
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://app.dub.co/integrations/zapier) when a new link is created in Dub
* Send a notification on [Slack](https://app.dub.co/integrations/slack) when a link is clicked
* For building gamified referral programs with [Dub Conversions](/conversions/quickstart) – e.g. increment usage credits for both the referrer and referee when a conversion event 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.
4. **Events**: Select the events you want to listen to. You can select multiple events. Refer to the [Available Events](#available-events) section to see the list of available events.
Finally, click on **Create webhook** to create the webhook.
## Available webhook events
When setting up a webhook, you can select the events you want to listen to across your Dub workspace. There are two types of events you can listen to:
* **Workspace-level events**: These events are triggered in the context of your entire workspace.
* **Link-level events**: These events are triggered in the context of a specific link.
### Workspace-level events
These events are triggered in the context of your entire workspace:
This event is triggered when a [new link is created](/api-reference/endpoint/create-a-new-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"
}
}
```
This event is triggered when a link is updated 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"
}
}
```
This event is triggered when a link is deleted 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"
}
}
```
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"
}
}
}
```
This event is triggered when a [new sale is created](/api-reference/endpoint/create-a-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
}
}
}
```
### Link-level events
Due to the high volume nature of these events, they 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.
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"
}
}
}
```
## 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.
## 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:
## 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.
Webhooks are retried 3 times with an exponential backoff. You can see all the retry attempts in the webhook event logs.
## Signature Verification
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
```
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.
```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"))
}
```
# Introduction
Dub is the link management platform for modern marketing teams.
Our mission is to reimagine the role of links – one of the most foundational pieces of the web – from being a simple **"resource locator"** (URL) to a full **attribution engine** – visualizing the user journey from the first click to the final conversion event.
## Features
Here are some of the features that Dub offers:
Dub provides powerful analytics for your links, including geolocation,
device, browser, and referrer information. [Learn
more](https://dub.co/help/article/dub-analytics)
Dub offers free custom domains on all plans for you to create branded links
that stand out. [Learn more](https://dub.co/features/branded-links)
QR codes and short links are like peas in a pod. Dub offers free QR codes
for every short link you create – both via our dashboard and
[programmatically](/api-reference/endpoint/retrieve-a-qr-code). [Learn
more](https://dub.co/features/qr-codes)
Customize your link's behavior with [device
targeting](https://dub.co/help/article/device-targeting), [geo
targeting](https://dub.co/help/article/geo-targeting), [link
expiration](https://dub.co/help/article/link-expiration), [link
cloaking](https://dub.co/help/article/link-cloaking), and more. [Learn
more](https://dub.co/features/personalization)
Invite your teammates to collaborate on your links. For enterprises, Dub
offers [SAML SSO](https://dub.co/help/category/saml-sso) for better
security. [Learn more](https://dub.co/features/collaboration)
## Getting Started
To get started with Dub, you can do any of the following:
Follow our quickstart guide to get started with Dub
Learn how to use Dub's API to create and manage links programmatically
Learn how to self-host Dub for greater control and privacy
Learn how to integrate Dub with your favorite tools
# Local Development
A guide on how to run Dub.co'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
├── 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:
* `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:
* Link redirects: [`main`/apps/web/lib/middleware/link.ts](https://github.com/dubinc/dub/blob/main/apps/web/lib/middleware/link.ts).
* Root domain redirects: [`main`/apps/web/lib/middleware/root.ts](https://github.com/dubinc/dub/blob/main/apps/web/lib/middleware/root.ts).
## Running Dub locally
To run Dub.co 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
## Step 1: Local setup
First, you'll need to clone the Dub.co repo and install the dependencies.
First, clone the [Dub.co 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 to `.env`:
```bash Terminal
cp .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.
In your newly-cloned Dub.co 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.
![Upstash Redis database](https://mintlify.s3-us-west-1.amazonaws.com/dub/images/upstash-create-db.png)
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.
![Upstash Redis tokens](https://mintlify.s3-us-west-1.amazonaws.com/dub/images/upstash-redis-tokens.png)
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.
![Upstash QStash tokens](https://mintlify.s3-us-west-1.amazonaws.com/dub/images/upstash-qstash-tokens.png)
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.
Add the following credentials to your `.env` file:
```
PLANETSCALE_DATABASE_URL="http://root:unused@localhost:3900"
DATABASE_URL="mysql://root:@localhost:3306/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
npx prisma generate
```
Then, create the database tables with the following command:
```bash Terminal
npx prisma db 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**.
![PlanetScale choose framework](https://mintlify.s3-us-west-1.amazonaws.com/dub/images/planetscale-choose-framework.png)
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.
![PlanetScale add credentials](https://mintlify.s3-us-west-1.amazonaws.com/dub/images/planetscale-add-credentials.png)
In the terminal, navigate to the `apps/web` directory and run the following command to generate the Prisma client:
```bash Terminal
npx prisma generate
```
Then, create the database tables with the following command:
```bash Terminal
npx prisma db 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: 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).
# Django
Learn how to integrate Dub with Django.
## 1. Prerequisites
To follow this guide, you will need to:
* [Create a Dub account](https://d.to/try)
* [Create an API key](https://app.dub.co/settings/tokens)
## 2. Install and initialize the Dub Python SDK
To install the [Dub Python SDK](https://dub.co/solutions/python), run the following command:
```bash pip
pip install dub
```
Initialize the Dub Python SDK by creating a new instance of the `Dub` class.
```python
import os
import dub
from dub.models import operations
from django.http import JsonResponse
d = dub.Dub(
token=os.environ['DUB_API_KEY'],
)
```
## 3. Create link
Let's create a short link using the [Dub Python SDK](https://dub.co/solutions/python).
```python views.py
def create_link(request):
res = d.links.create(request={
"url": "https://google.com",
})
return JsonResponse({
"short_link": res.short_link
})
```
The destination URL of the short link.
The domain of the short link. If not provided, the primary domain for the
workspace will be used (or `dub.sh` if the workspace has no domains).
The short link slug. If not provided, a random 7-character slug will be
generated.
This is the ID of the link in your database. If set, it can be used to
identify the link in the future.
The prefix of the short link slug for randomly-generated keys (e.g. if prefix
is `/c/`, generated keys will be in the `/c/:key` format). Will be ignored if
key is provided.
Whether the short link is archived.
Whether the short link's stats are publicly accessible.
The unique ID of the tag assigned to the short link. This field is deprecated
– use `tagIds` instead.
The unique IDs of the tags assigned to the short link.
The comments for the short link.
The date and time when the short link will expire at.
The URL to redirect to when the short link has expired.
The password required to access the destination URL of the short link.
Whether the short link uses Custom Social Media Cards feature.
The title of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
The description of the short link generated via `api.dub.co/metatags`. Will be
used for Custom Social Media Cards if proxy is true.
The image of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
Whether the short link uses link cloaking.
The iOS destination URL for the short link for iOS device targeting.
The Android destination URL for the short link for Android device targeting.
The UTM source of the short link. If set, this will populate or override the
UTM source in the destination URL.
The UTM medium of the short link. If set, this will populate or override the
UTM medium in the destination URL.
The UTM campaign of the short link. If set, this will populate or override the
UTM campaign in the destination URL.
The UTM term of the short link. If set, this will populate or override the UTM
term in the destination URL.
The UTM content of the short link. If set, this will populate or override the
UTM content in the destination URL.
Optionally, you can also pass an `externalId` field which is a unique identifier for the link in your own database to associate it with the link in Dub's system.
```python views.py
def create_link(request):
res = d.links.create(request={
"url": "https://google.com",
"external_id": "12345",
})
return JsonResponse({
"short_link": res.short_link
})
```
This will let you easily [update the link](#5-update-link) or [retrieve analytics](#6-retrieve-analytics-for-link) for it later on using the `externalId` instead of the Dub `linkId`.
## 4. Upsert link
Dub Python SDK provides a method to upsert a link – where an existing link is updated if it exists, or a new link is created if it doesn't. so you don't have to worry about checking if the link already exists.
```python views.py
def upsert_link(request):
res = d.links.upsert(request={
"url": "https://google.com",
})
return JsonResponse({
"short_link": res.short_link
})
```
This way, you won't have to worry about checking if the link already exists when you're creating it.
## 5. Update link
Let's update an existing link using the Dub Python SDK.
You can do that in two ways:
* Using the link's `linkId` in Dub's system.
* Using the link's `externalId` in your own database (prefixed with `ext_`).
```python views.py
# Update a link by its linkId
def update_link(request):
res = d.links.update(link_id="clx1gvi9o0005hf5momm6f7hj", request_body={
"url": "https://google.uk",
})
return JsonResponse({
"short_link": res.short_link
})
# Update a link by its externalId
def update_link(request):
res = d.links.update(external_id="ext_12345", request_body={
"url": "https://google.uk",
})
return JsonResponse({
"short_link": res.short_link
})
```
## 6. Retrieve analytics for link
Dub allows you to retrieve analytics for a link using the Dub Python SDK.
```python views.py
# Retrieve the timeseries analytics for the last 7 days for a link
def analytics(request):
res = d.analytics.retrieve(request={
"link_id": "clx1gvi9o0005hf5momm6f7hj",
"interval": "7d",
"group_by": "timeseries",
})
return JsonResponse(res)
```
Similarly, you can retrieve analytics for a link using the `externalId` field.
```python views.py
# Retrieve the timeseries analytics for the last 7 days for a link
def analytics(request):
res = d.analytics.retrieve(request={
"external_id": "ext_12345",
"interval": "7d",
"group_by": "timeseries",
})
return JsonResponse(res)
```
## 7. Examples
See the full example on GitHub.
# Express
Learn how to integrate Dub with Express.
## 1. Prerequisites
To follow this guide, you will need to:
* [Create a Dub account](https://d.to/try)
* [Create an API key](https://app.dub.co/settings/tokens)
## 2. Install and initialize the Dub TypeScript SDK
Install the [Dub TypeScript SDK](/sdks/typescript/overview) using your preferred package manager:
```bash
npm install dub
```
```bash
yarn add dub zod # zod is a peer dependency
```
```bash
pnpm add dub
```
Then, initialize the Dub TypeScript SDK with your API key.
```typescript lib/dub.ts
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY, // optional, defaults to DUB_API_KEY env variable
});
```
You can now use the `dub` object to interact with the Dub API.
```typescript
import { dub } from "./lib/dub";
```
## 3. Create link
Let's create a short link using the [Dub TypeScript SDK](/sdks/typescript/overview).
```typescript index.ts
app.post("/create-link", async (req: Request, res: Response) => {
try {
const result = await dub.links.create({
url: "https://www.google.com",
});
res.status(200).json(result);
} catch (error: any) {
res.status(400).json(error);
}
});
```
The destination URL of the short link.
The domain of the short link. If not provided, the primary domain for the
workspace will be used (or `dub.sh` if the workspace has no domains).
The short link slug. If not provided, a random 7-character slug will be
generated.
This is the ID of the link in your database. If set, it can be used to
identify the link in the future.
The prefix of the short link slug for randomly-generated keys (e.g. if prefix
is `/c/`, generated keys will be in the `/c/:key` format). Will be ignored if
key is provided.
Whether the short link is archived.
Whether the short link's stats are publicly accessible.
The unique ID of the tag assigned to the short link. This field is deprecated
– use `tagIds` instead.
The unique IDs of the tags assigned to the short link.
The comments for the short link.
The date and time when the short link will expire at.
The URL to redirect to when the short link has expired.
The password required to access the destination URL of the short link.
Whether the short link uses Custom Social Media Cards feature.
The title of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
The description of the short link generated via `api.dub.co/metatags`. Will be
used for Custom Social Media Cards if proxy is true.
The image of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
Whether the short link uses link cloaking.
The iOS destination URL for the short link for iOS device targeting.
The Android destination URL for the short link for Android device targeting.
The UTM source of the short link. If set, this will populate or override the
UTM source in the destination URL.
The UTM medium of the short link. If set, this will populate or override the
UTM medium in the destination URL.
The UTM campaign of the short link. If set, this will populate or override the
UTM campaign in the destination URL.
The UTM term of the short link. If set, this will populate or override the UTM
term in the destination URL.
The UTM content of the short link. If set, this will populate or override the
UTM content in the destination URL.
Optionally, you can also pass an `externalId` field which is a unique identifier for the link in your own database to associate it with the link in Dub's system.
```typescript index.ts
app.post("/create-link", async (req: Request, res: Response) => {
try {
const result = await dub.links.create({
url: "https://www.google.com",
externalId: "12345",
});
res.status(200).json(result);
} catch (error: any) {
res.status(400).json(error);
}
});
```
This will let you easily [update the link](#5-update-link) or [retrieve analytics](#6-retrieve-analytics-for-link) for it later on using the `externalId` instead of the Dub `linkId`.
## 4. Upsert link
Dub TypeScript SDK provides a method to upsert a link – where an existing link is updated if it exists, or a new link is created if it doesn't. so you don't have to worry about checking if the link already exists.
```typescript index.ts
app.post("/upsert-link", async (req: Request, res: Response) => {
try {
// Update the link if same URL already exists or create a new link
const result = await dub.links.upsert({
url: "https://www.google.com",
});
res.status(200).json(result);
} catch (error: any) {
res.status(400).json(error);
}
});
```
This way, you won't have to worry about checking if the link already exists when you're creating it.
## 5. Update link
Let's update an existing link using the Dub TypeScript SDK.
You can do that in two ways:
* Using the link's `linkId` in Dub's system.
* Using the link's `externalId` in your own database (prefixed with `ext_`).
```typescript index.ts
app.post("/update-link", async (req: Request, res: Response) => {
try {
// Update a link by its linkId
const { shortLink } = await dub.links.update("clv3o9p9q000au1h0mc7r6l63", {
url: "https://www.google.uk", // new URL
});
// Update a link by its externalId
const { shortLink } = await dub.links.update("ext_12345", {
url: "https://www.google.uk", // new URL
});
res.status(200).json({ shortLink });
} catch (error: any) {
res.status(400).json(error);
}
});
```
## 6. Retrieve analytics for link
Dub allows you to retrieve analytics for a link using the Dub TypeScript SDK.
```typescript index.ts
import { ClicksTimeseries } from "dub/models/components";
app.get("/analytics", async (req: Request, res: Response) => {
try {
// Retrieve the timeseries analytics for the last 7 days for a link
const response = await dub.analytics.retrieve({
linkId: "clv3o9p9q000au1h0mc7r6l63",
interval: "7d",
groupBy: "timeseries",
});
const timeseries = response as ClicksTimeseries[];
res.status(200).json(timeseries);
} catch (error: any) {
res.status(400).json(error);
}
});
```
Similarly, you can retrieve analytics for a link using the `externalId` field.
```typescript index.ts
// Retrieve the timeseries analytics for the last 7 days for a link
const response = await dub.analytics.retrieve({
externalId: "ext_12345",
interval: "7d",
groupBy: "timeseries",
});
const timeseries = response as ClicksTimeseries[];
```
## 7. Examples
See the full example on GitHub.
# Flask
Learn how to integrate Dub with Flask.
## 1. Prerequisites
To follow this guide, you will need to:
* [Create a Dub account](https://d.to/try)
* [Create an API key](https://app.dub.co/settings/tokens)
## 2. Install and initialize the Dub Python SDK
To install the [Dub Python SDK](https://dub.co/solutions/python), run the following command:
```bash pip
pip install dub
```
Initialize the Dub Python SDK by creating a new instance of the `Dub` class.
```python
import os
import dub
from dub.models import operations
d = dub.Dub(
token=os.environ['DUB_API_KEY'],
)
```
## 3. Create link
Let's create a short link using the [Dub Python SDK](https://dub.co/solutions/python).
```python app.py
def create_link():
res = d.links.create(request={
"url": "https://google.com",
})
return res.short_link
```
The destination URL of the short link.
The domain of the short link. If not provided, the primary domain for the
workspace will be used (or `dub.sh` if the workspace has no domains).
The short link slug. If not provided, a random 7-character slug will be
generated.
This is the ID of the link in your database. If set, it can be used to
identify the link in the future.
The prefix of the short link slug for randomly-generated keys (e.g. if prefix
is `/c/`, generated keys will be in the `/c/:key` format). Will be ignored if
key is provided.
Whether the short link is archived.
Whether the short link's stats are publicly accessible.
The unique ID of the tag assigned to the short link. This field is deprecated
– use `tagIds` instead.
The unique IDs of the tags assigned to the short link.
The comments for the short link.
The date and time when the short link will expire at.
The URL to redirect to when the short link has expired.
The password required to access the destination URL of the short link.
Whether the short link uses Custom Social Media Cards feature.
The title of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
The description of the short link generated via `api.dub.co/metatags`. Will be
used for Custom Social Media Cards if proxy is true.
The image of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
Whether the short link uses link cloaking.
The iOS destination URL for the short link for iOS device targeting.
The Android destination URL for the short link for Android device targeting.
The UTM source of the short link. If set, this will populate or override the
UTM source in the destination URL.
The UTM medium of the short link. If set, this will populate or override the
UTM medium in the destination URL.
The UTM campaign of the short link. If set, this will populate or override the
UTM campaign in the destination URL.
The UTM term of the short link. If set, this will populate or override the UTM
term in the destination URL.
The UTM content of the short link. If set, this will populate or override the
UTM content in the destination URL.
Optionally, you can also pass an `externalId` field which is a unique identifier for the link in your own database to associate it with the link in Dub's system.
```python app.py
def create_link(request):
res = d.links.create(request={
"url": "https://google.com",
"external_id": "12345",
})
return res.short_link
```
This will let you easily [update the link](#5-update-link) or [retrieve analytics](#6-retrieve-analytics-for-link) for it later on using the `externalId` instead of the Dub `linkId`.
## 4. Upsert link
Dub Python SDK provides a method to upsert a link – where an existing link is updated if it exists, or a new link is created if it doesn't. so you don't have to worry about checking if the link already exists.
```python app.py
def upsert_link(request):
res = d.links.upsert(request={
"url": "https://google.com",
})
return res.short_link
```
This way, you won't have to worry about checking if the link already exists when you're creating it.
## 5. Update link
Let's update an existing link using the Dub Python SDK.
You can do that in two ways:
* Using the link's `linkId` in Dub's system.
* Using the link's `externalId` in your own database (prefixed with `ext_`).
```python app.py
# Update a link by its linkId
def update_link(request):
res = d.links.update(link_id="clx1gvi9o0005hf5momm6f7hj", request_body={
"url": "https://google.uk",
})
return res.short_link
# Update a link by its externalId
def update_link(request):
res = d.links.update(external_id="ext_12345", request_body={
"url": "https://google.uk",
})
return res.short_link
```
## 6. Retrieve analytics for link
Dub allows you to retrieve analytics for a link using the Dub Python SDK.
```python app.py
# Retrieve the timeseries analytics for the last 7 days for a link
def analytics(request):
res = d.analytics.retrieve(request={
"link_id": "clx1gvi9o0005hf5momm6f7hj",
"interval": "7d",
"group_by": "timeseries",
})
return str(res)
```
Similarly, you can retrieve analytics for a link using the `externalId` field.
```python views.py
# Retrieve the timeseries analytics for the last 7 days for a link
def analytics(request):
res = d.analytics.retrieve(request={
"external_id": "ext_12345",
"interval": "7d",
"group_by": "timeseries",
})
return str(res)
```
## 7. Examples
See the full example on GitHub.
# Introduction
Learn how to integrate Dub with Go.
## 1. Prerequisites
To follow this guide, you will need to:
* [Create a Dub account](https://d.to/try)
* [Create an API key](https://app.dub.co/settings/tokens)
## 2. Install and initialize the Dub Go SDK
To install the [Dub Go SDK](https://dub.co/solutions/go), run the following command:
```bash bash
go get github.com/dubinc/dub-go
```
Initialize the Dub Go SDK by creating a new instance of the `Dub` struct.
```go
package main
import (
"log"
"os"
"context"
dub "github.com/dubinc/dub-go"
)
d := dub.New(
dub.WithSecurity(os.Getenv("DUB_API_KEY")),
)
```
## 3. Create link
Let's create a short link using the [Dub Go SDK](https://dub.co/solutions/go).
```go main.go
func main() {
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)
}
}
```
The destination URL of the short link.
The domain of the short link. If not provided, the primary domain for the
workspace will be used (or `dub.sh` if the workspace has no domains).
The short link slug. If not provided, a random 7-character slug will be
generated.
This is the ID of the link in your database. If set, it can be used to
identify the link in the future.
The prefix of the short link slug for randomly-generated keys (e.g. if prefix
is `/c/`, generated keys will be in the `/c/:key` format). Will be ignored if
key is provided.
Whether the short link is archived.
Whether the short link's stats are publicly accessible.
The unique ID of the tag assigned to the short link. This field is deprecated
– use `tagIds` instead.
The unique IDs of the tags assigned to the short link.
The comments for the short link.
The date and time when the short link will expire at.
The URL to redirect to when the short link has expired.
The password required to access the destination URL of the short link.
Whether the short link uses Custom Social Media Cards feature.
The title of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
The description of the short link generated via `api.dub.co/metatags`. Will be
used for Custom Social Media Cards if proxy is true.
The image of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
Whether the short link uses link cloaking.
The iOS destination URL for the short link for iOS device targeting.
The Android destination URL for the short link for Android device targeting.
The UTM source of the short link. If set, this will populate or override the
UTM source in the destination URL.
The UTM medium of the short link. If set, this will populate or override the
UTM medium in the destination URL.
The UTM campaign of the short link. If set, this will populate or override the
UTM campaign in the destination URL.
The UTM term of the short link. If set, this will populate or override the UTM
term in the destination URL.
The UTM content of the short link. If set, this will populate or override the
UTM content in the destination URL.
Optionally, you can also pass an `externalId` field which is a unique identifier for the link in your own database to associate it with the link in Dub's system.
```go main.go
func main() {
request := &operations.CreateLinkRequestBody{
URL: "https://google.com",
ExternalId: "12345"
}
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)
}
}
```
This will let you easily [update the link](#5-update-link) or [retrieve analytics](#6-retrieve-analytics-for-link) for it later on using the `externalId` instead of the Dub `linkId`.
## 4. Upsert link
Dub Go SDK provides a method to upsert a link – where an existing link is updated if it exists, or a new link is created if it doesn't. so you don't have to worry about checking if the link already exists.
```go main.go
func main() {
// Update the link if same URL already exists or create a new link
request := &operations.UpsertLinkRequestBody{
URL: "https://google.com",
}
ctx := context.Background()
res, err := d.Links.Upsert(ctx, request)
if err != nil {
log.Fatal(err)
}
if res.LinkSchema != nil {
fmt.Println(res.LinkSchema.ShortLink)
}
}
```
This way, you won't have to worry about checking if the link already exists when you're creating it.
## 5. Update link
Let's update an existing link using the Dub Go SDK.
You can do that in two ways:
* Using the link's `linkId` in Dub's system.
* Using the link's `externalId` in your own database (prefixed with `ext_`).
```go main.go
func main() {
request := &operations.UpdateLinkRequestBody{
URL: "https://google.us",
}
// Update a link by its linkId
ctx := context.Background()
res, err := d.Links.Update(ctx, "clv3o9p9q000au1h0mc7r6l63", request)
if err != nil {
log.Fatal(err)
}
if res.LinkSchema != nil {
fmt.Println(res.LinkSchema.ShortLink)
}
}
```
## 6. Retrieve analytics for link
Let's retrieve analytics for a link using the Dub Go SDK.
```go main.go
func main() {
// Retrieve the timeseries analytics for the last 7 days for a link
request := operations.RetrieveAnalyticsRequest{
LinkId: "clv3o9p9q000au1h0mc7r6l63",
Interval: "7d",
GroupBy: "timeseries"
}
ctx := context.Background()
res, err := d.Analytics.Retrieve(ctx, request)
if err != nil {
log.Fatal(err)
}
if res.OneOf != nil {
// handle response
}
}
```
## 7. Examples
See the full example on GitHub.
# Laravel
Learn how to integrate Dub with Laravel.
## 1. Prerequisites
To follow this guide, you will need to:
* [Create a Dub account](https://d.to/try)
* [Create an API key](https://app.dub.co/settings/tokens)
## 2. Install and initialize the Dub PHP SDK
To install the [Dub PHP SDK](https://dub.co/solutions/php), run the following command:
```bash
composer require dub/dub-php
```
In your `.env` file, add your Dub API key:
```bash
DUB_API_KEY=your_api_key
```
In your `config/services.php` file, add the following:
```php
'dub' => [
'api_key' => env('DUB_API_KEY'),
],
```
You can now create an instance of the `Dub` class and pass in your API key:
```php index.php
use Dub\Dub;
use Dub\Components\Security;
$dub = Dub::builder()->setSecurity(config('services.dub.api_key'))->build();
// create a link
$dub->links->create(...);
```
If you want to be able to inject the `Dub` class via the service container, add this to the `register` method of your `AppServiceProvider.php`:
```php index.php
$this->app->bind(Dub::class, function ($app) {
return Dub::builder()->setSecurity($app['config']->get('services.dub.api_key'))->build();
});
```
You can then inject the authenticated `Dub` instance throughout your application:
```php index.php
use Dub\Laravel\Dub;
class LinkController extends Controller {
public function createLink(Dub $dub) {
// Now you can use the SDK instance
$dub->links->create(...);
}
}
```
## 3. Create link
Let's create a short link using the Dub Laravel SDK.
```php index.php
use Dub\Models\Operations;
class LinkController extends Controller {
public function createLink() {
$dub = new Dub();
try {
$request = new Operations\CreateLinkRequestBody(
url: 'https://google.com'
);
$response = $dub->links->create($request);
if ($response->linkSchema !== null) {
// handle response
}
} catch (Throwable $e) {
// handle exception
}
}
}
```
The destination URL of the short link.
The domain of the short link. If not provided, the primary domain for the
workspace will be used (or `dub.sh` if the workspace has no domains).
The short link slug. If not provided, a random 7-character slug will be
generated.
This is the ID of the link in your database. If set, it can be used to
identify the link in the future.
The prefix of the short link slug for randomly-generated keys (e.g. if prefix
is `/c/`, generated keys will be in the `/c/:key` format). Will be ignored if
key is provided.
Whether the short link is archived.
Whether the short link's stats are publicly accessible.
The unique ID of the tag assigned to the short link. This field is deprecated
– use `tagIds` instead.
The unique IDs of the tags assigned to the short link.
The comments for the short link.
The date and time when the short link will expire at.
The URL to redirect to when the short link has expired.
The password required to access the destination URL of the short link.
Whether the short link uses Custom Social Media Cards feature.
The title of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
The description of the short link generated via `api.dub.co/metatags`. Will be
used for Custom Social Media Cards if proxy is true.
The image of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
Whether the short link uses link cloaking.
The iOS destination URL for the short link for iOS device targeting.
The Android destination URL for the short link for Android device targeting.
The UTM source of the short link. If set, this will populate or override the
UTM source in the destination URL.
The UTM medium of the short link. If set, this will populate or override the
UTM medium in the destination URL.
The UTM campaign of the short link. If set, this will populate or override the
UTM campaign in the destination URL.
The UTM term of the short link. If set, this will populate or override the UTM
term in the destination URL.
The UTM content of the short link. If set, this will populate or override the
UTM content in the destination URL.
Optionally, you can also pass an `externalId` field to associate the link with a unique identifier in your own system.
```php index.php
use Dub\Models\Operations;
class LinkController extends Controller
{
public function createLinkWithExternalId()
{
$dub = new Dub();
try {
$request = new Operations\CreateLinkRequestBody(
url: 'https://google.com',
externalId: '12345'
);
$response = $dub->links->create($request);
if ($response->linkSchema !== null) {
// handle response
}
} catch (Throwable $e) {
// handle exception
}
}
}
```
This will let you easily [update the link](#5-update-link) or [retrieve analytics](#6-retrieve-analytics-for-link) for it later on using the `externalId` instead of the Dub `linkId`.
## 4. Upsert link
The Dub Laravel SDK provides a method to upsert a link – where an existing link is updated if it exists, or a new link is created if it doesn't.
```php index.php
use Dub\Models\Operations;
class LinkController extends Controller
{
public function upsertLink()
{
$dub = new Dub();
try {
$request = new Operations\UpsertLinkRequestBody(
url: 'https://google.com'
);
$response = $dub->links->upsert($request);
if ($response->linkSchema !== null) {
// handle response
}
} catch (Throwable $e) {
// handle exception
}
}
}
```
This way, you won't have to worry about checking if the link already exists when you're creating it.
## 5. Update link
To update an existing link using the Dub Laravel SDK, you can either use the link's `linkId` or `externalId`.
```php index.php
use Dub\Models\Operations;
class LinkController extends Controller
{
public function updateLink()
{
$dub = new Dub();
try {
$request = new Operations\UpdateLinkRequest();
$request->linkId = 'cly2p8onm000cym8200nfay7l';
$request->requestBody = new Operations\UpdateLinkRequestBody();
$request->requestBody->url = 'https://google.us';
$response = $dub->links->update($request);
if ($response->linkSchema !== null) {
echo $response->linkSchema->shortLink;
}
} catch (Throwable $e) {
// handle exception
}
}
}
```
## 6. Retrieve analytics for link
You can also retrieve analytics for a link using the Dub Laravel SDK.
```php index.php
use Dub\Models\Operations;
class LinkController extends Controller
{
public function retrieveAnalytics()
{
$dub = new Dub();
try {
$request = new Operations\RetrieveAnalyticsRequest();
$request->linkId = 'clmnr6jcc0005l308q9v56uz1';
$request->interval = Operations\Interval::SevenD;
$request->groupBy = Operations\GroupBy::Timeseries;
$response = $dub->analytics->retrieve($request);
if ($response->oneOf !== null) {
// Handle the response
print_r($response->oneOf);
}
} catch (Throwable $e) {
// handle exception
}
}
}
```
## 7. Examples
See the full example on GitHub.
# Next.js
Learn how to integrate Dub with Next.js.
## 1. Prerequisites
To follow this guide, you will need to:
* [Create a Dub account](https://d.to/try)
* [Create an API key](https://app.dub.co/settings/tokens)
## 2. Install and initialize the Dub TypeScript SDK
Install the [Dub TypeScript SDK](/sdks/typescript/overview) using your preferred package manager:
```bash
npm install dub
```
```bash
yarn add dub zod # zod is a peer dependency
```
```bash
pnpm add dub
```
Then, initialize the Dub TypeScript SDK with your API key.
```typescript lib/dub.ts
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY, // optional, defaults to DUB_API_KEY env variable
});
```
You can now use the `dub` object to interact with the Dub API.
```typescript
import { dub } from "./lib/dub";
```
## 3. Create link
Let's create a short link using the [Dub TypeScript SDK](/sdks/typescript/overview).
```typescript app/api/links/route.ts
export async function POST() {
try {
const link = await dub.links.create({
url: "https://google.com",
});
return Response.json(link);
} catch (error) {
return Response.json({ error }, { status: 500 });
}
}
```
The destination URL of the short link.
The domain of the short link. If not provided, the primary domain for the
workspace will be used (or `dub.sh` if the workspace has no domains).
The short link slug. If not provided, a random 7-character slug will be
generated.
This is the ID of the link in your database. If set, it can be used to
identify the link in the future.
The prefix of the short link slug for randomly-generated keys (e.g. if prefix
is `/c/`, generated keys will be in the `/c/:key` format). Will be ignored if
key is provided.
Whether the short link is archived.
Whether the short link's stats are publicly accessible.
The unique ID of the tag assigned to the short link. This field is deprecated
– use `tagIds` instead.
The unique IDs of the tags assigned to the short link.
The comments for the short link.
The date and time when the short link will expire at.
The URL to redirect to when the short link has expired.
The password required to access the destination URL of the short link.
Whether the short link uses Custom Social Media Cards feature.
The title of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
The description of the short link generated via `api.dub.co/metatags`. Will be
used for Custom Social Media Cards if proxy is true.
The image of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
Whether the short link uses link cloaking.
The iOS destination URL for the short link for iOS device targeting.
The Android destination URL for the short link for Android device targeting.
The UTM source of the short link. If set, this will populate or override the
UTM source in the destination URL.
The UTM medium of the short link. If set, this will populate or override the
UTM medium in the destination URL.
The UTM campaign of the short link. If set, this will populate or override the
UTM campaign in the destination URL.
The UTM term of the short link. If set, this will populate or override the UTM
term in the destination URL.
The UTM content of the short link. If set, this will populate or override the
UTM content in the destination URL.
Optionally, you can also pass an `externalId` field which is a unique identifier for the link in your own database to associate it with the link in Dub's system.
```typescript app/api/links/route.ts
export async function POST() {
try {
const link = await dub.links.create({
url: "https://google.com",
externalId: "12345",
});
return Response.json(link);
} catch (error) {
return Response.json({ error }, { status: 500 });
}
}
```
This will let you easily [update the link](#5-update-link) or [retrieve analytics](#6-retrieve-analytics-for-link) for it later on using the `externalId` instead of the Dub `linkId`.
## 4. Upsert link
Dub TypeScript SDK provides a method to upsert a link – where an existing link is updated if it exists, or a new link is created if it doesn't. so you don't have to worry about checking if the link already exists.
```typescript app/api/links/route.ts
export async function PATCH() {
try {
const { shortLink } = await dub.links.upsert({
url: "https://google.com",
});
return Response.json({ shortLink }); // will always be the same short link
} catch (error) {
return Response.json({ error }, { status: 500 });
}
}
```
This way, you won't have to worry about checking if the link already exists when you're creating it.
## 5. Update link
Let's update an existing link using the Dub TypeScript SDK.
You can do that in two ways:
* Using the link's `linkId` in Dub's system.
* Using the link's `externalId` in your own database (prefixed with `ext_`).
```typescript app/api/links/route.ts
export async function PATCH() {
try {
// Update a link by its linkId
const { shortLink } = await dub.links.update("clv3o9p9q000au1h0mc7r6l63", {
url: "https://www.google.uk", // new URL
});
// Update a link by its externalId
const { shortLink } = await dub.links.update("ext_12345", {
url: "https://www.google.uk", // new URL
});
return Response.json({ shortLink });
} catch (error) {
return Response.json({ error }, { status: 500 });
}
}
```
## 6. Retrieve analytics for link
Dub allows you to retrieve analytics for a link using the Dub TypeScript SDK.
```typescript app/api/analytics/route.ts
import { ClicksTimeseries } from "dub/models/components";
export async function GET() {
try {
// Retrieve the timeseries analytics for the last 7 days for a link
const response = await dub.analytics.retrieve({
linkId: "clv3o9p9q000au1h0mc7r6l63",
interval: "7d",
groupBy: "timeseries",
});
const timeseries = response as ClicksTimeseries[];
return Response.json(timeseries);
} catch (error) {
return Response.json({ error }, { status: 500 });
}
}
```
Similarly, you can retrieve analytics for a link using the `externalId` field.
```typescript app/api/analytics/route.ts
// Retrieve the timeseries analytics for the last 7 days for a link
const response = await dub.analytics.retrieve({
externalId: "ext_12345",
interval: "7d",
groupBy: "timeseries",
});
const timeseries = response as ClicksTimeseries[];
```
## 7. Examples
See the full example on GitHub.
# Nuxt
Learn how to integrate Dub with Nuxt.
## 1. Prerequisites
To follow this guide, you will need to:
* [Create a Dub account](https://d.to/try)
* [Create an API key](https://app.dub.co/settings/tokens)
## 2. Install and initialize the Dub TypeScript SDK
Install the [Dub TypeScript SDK](/sdks/typescript/overview) using your preferred package manager:
```bash
npm install dub
```
```bash
yarn add dub zod # zod is a peer dependency
```
```bash
pnpm add dub
```
Then, initialize the Dub TypeScript SDK with your API key.
```typescript lib/dub.ts
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY, // optional, defaults to DUB_API_KEY env variable
});
```
You can now use the `dub` object to interact with the Dub API.
```typescript
import { dub } from "./lib/dub";
```
## 3. Create link
Let's create a short link using the [Dub TypeScript SDK](/sdks/typescript/overview).
```typescript server/api/links.post.ts
export default defineEventHandler(async () => {
try {
const result = await dub.links.create({
url: "https://www.google.com",
});
return result;
} catch (error) {
console.error(error);
return error;
}
});
```
Optionally, you can also pass an `externalId` field which is a unique identifier for the link in your own database to associate it with the link in Dub's system.
```typescript server/api/links.post.ts
export default defineEventHandler(async () => {
try {
const result = await dub.links.create({
url: "https://www.google.com",
externalId: "12345",
});
return result;
} catch (error) {
console.error(error);
return error;
}
});
```
This will let you easily [update the link](#5-update-link) or [retrieve analytics](#6-retrieve-analytics-for-link) for it later on using the `externalId` instead of the Dub `linkId`.
## 4. Upsert link
Dub TypeScript SDK provides a method to upsert a link – where an existing link is updated if it exists, or a new link is created if it doesn't. so you don't have to worry about checking if the link already exists.
```typescript server/api/links.put.ts
export default defineEventHandler(async () => {
try {
const result = await dub.links.upsert({
url: "https://www.google.com",
});
return result;
} catch (error) {
console.error(error);
return error;
}
});
```
This way, you won't have to worry about checking if the link already exists when you're creating it.
## 5. Update link
Let's update an existing link using the Dub TypeScript SDK.
You can do that in two ways:
* Using the link's `linkId` in Dub's system.
* Using the link's `externalId` in your own database (prefixed with `ext_`).
```typescript server/api/links.patch.ts
export default defineEventHandler(async () => {
try {
// Update a link by its linkId
const { shortLink } = await dub.links.update("clv3o9p9q000au1h0mc7r6l63", {
url: "https://www.google.uk", // new URL
});
// Update a link by its externalId
const { shortLink } = await dub.links.update("ext_12345", {
url: "https://www.google.uk", // new URL
});
return { shortLink };
} catch (error) {
console.error(error);
return error;
}
});
```
## 6. Retrieve analytics for link
Dub allows you to retrieve analytics for a link using the Dub TypeScript SDK.
```typescript server/api/analytics.get.ts
import { ClicksTimeseries } from "dub/models/components";
export default defineEventHandler(async () => {
try {
// Retrieve the timeseries analytics for the last 7 days for a link
const response = await dub.analytics.retrieve({
linkId: "clv3o9p9q000au1h0mc7r6l63",
interval: "7d",
groupBy: "timeseries",
});
const timeseries = response as ClicksTimeseries[];
return timeseries;
} catch (error) {
console.error(error);
return error;
}
});
```
Similarly, you can retrieve analytics for a link using the `externalId` field.
```typescript server/api/analytics.get.ts
// Retrieve the timeseries analytics for the last 7 days for a link
const response = await dub.analytics.retrieve({
externalId: "ext_12345",
interval: "7d",
groupBy: "timeseries",
});
const timeseries = response as ClicksTimeseries[];
```
## 7. Examples
See the full example on GitHub.
# Introduction
Learn how to integrate Dub with PHP.
## 1. Prerequisites
To follow this guide, you will need to:
* [Create a Dub account](https://d.to/try)
* [Create an API key](https://app.dub.co/settings/tokens)
## 2. Install and initialize the Dub PHP SDK
To install the [Dub PHP SDK](https://dub.co/solutions/php), run the following command:
```bash
composer require dub/dub-php
```
Initialize the Dub PHP SDK by creating a new instance of the `Dub` class.
```php index.php
declare(strict_types=1);
require 'vendor/autoload.php';
use Dub\Dub;
use Dub\Models\Components;
$dub = Dub::builder()->setSecurity("DUB_API_KEY")->build();
```
## 3. Create link
Let's create a short link using the Dub PHP SDK.
```php index.php
use Dub\Models\Operations;
try {
$request = new Operations\CreateLinkRequestBody(
url: 'https://google.com',
);
$response = $dub->links->create($request);
if ($response->linkSchema !== null) {
// handle response
}
} catch (Throwable $e) {
// handle exception
}
```
The destination URL of the short link.
The domain of the short link. If not provided, the primary domain for the
workspace will be used (or `dub.sh` if the workspace has no domains).
The short link slug. If not provided, a random 7-character slug will be
generated.
This is the ID of the link in your database. If set, it can be used to
identify the link in the future.
The prefix of the short link slug for randomly-generated keys (e.g. if prefix
is `/c/`, generated keys will be in the `/c/:key` format). Will be ignored if
key is provided.
Whether the short link is archived.
Whether the short link's stats are publicly accessible.
The unique ID of the tag assigned to the short link. This field is deprecated
– use `tagIds` instead.
The unique IDs of the tags assigned to the short link.
The comments for the short link.
The date and time when the short link will expire at.
The URL to redirect to when the short link has expired.
The password required to access the destination URL of the short link.
Whether the short link uses Custom Social Media Cards feature.
The title of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
The description of the short link generated via `api.dub.co/metatags`. Will be
used for Custom Social Media Cards if proxy is true.
The image of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
Whether the short link uses link cloaking.
The iOS destination URL for the short link for iOS device targeting.
The Android destination URL for the short link for Android device targeting.
The UTM source of the short link. If set, this will populate or override the
UTM source in the destination URL.
The UTM medium of the short link. If set, this will populate or override the
UTM medium in the destination URL.
The UTM campaign of the short link. If set, this will populate or override the
UTM campaign in the destination URL.
The UTM term of the short link. If set, this will populate or override the UTM
term in the destination URL.
The UTM content of the short link. If set, this will populate or override the
UTM content in the destination URL.
Optionally, you can also pass an `externalId` field which is a unique identifier for the link in your own database to associate it with the link in Dub's system.
```php index.php
use Dub\Models\Operations;
try {
$request = new Operations\CreateLinkRequestBody(
url: 'https://google.com',
externalId: '12345',
);
$response = $dub->links->create($request);
if ($response->linkSchema !== null) {
// handle response
}
} catch (Throwable $e) {
// handle exception
}
```
This will let you easily [update the link](#5-update-link) or [retrieve analytics](#6-retrieve-analytics-for-link) for it later on using the `externalId` instead of the Dub `linkId`.
## 4. Upsert link
Dub PHP SDK provides a method to upsert a link – where an existing link is updated if it exists, or a new link is created if it doesn't. so you don't have to worry about checking if the link already exists.
```php index.php
use Dub\Models\Operations;
try {
$request = new Operations\UpsertLinkRequestBody(
url: 'https://google.com',
);
$response = $dub->links->upsert($request);
if ($response->linkSchema !== null) {
// handle response
}
} catch (Throwable $e) {
// handle exception
}
```
This way, you won't have to worry about checking if the link already exists when you're creating it.
## 5. Update link
Let's update an existing link using the Dub PHP SDK.
You can do that in two ways:
* Using the link's `linkId` in Dub's system.
* Using the link's `externalId` in your own database (prefixed with ext\_).
```php index.php
use Dub\Models\Operations;
try {
$request = new Operations\UpdateLinkRequest();
$request->linkId = 'cly2p8onm000cym8200nfay7l';
$request->requestBody = new Operations\UpdateLinkRequestBody();
$request->requestBody->url = 'https://google.us';
$response = $dub->links->update($request);
if ($response->linkSchema !== null) {
echo $response->linkSchema->shortLink;
}
} catch (Throwable $e) {
// handle exception
}
```
## 6. Retrieve analytics for link
Let's retrieve analytics for a link using the Dub PHP SDK.
```php index.php
use Dub\Models\Operations;
try {
$request = new Operations\RetrieveAnalyticsRequest();
$request->linkId = 'clmnr6jcc0005l308q9v56uz1';
$request->interval = Operations\Interval::SevenD;
$request->groupBy = Operations\GroupBy::Timeseries;
$response = $dub->analytics->retrieve($request);
if ($response->oneOf !== null) {
// Handle the response
print_r($response->oneOf);
}
} catch (Throwable $e) {
// handle exception
}
```
## 7. Examples
See the full example on GitHub.
# Introduction
Learn how to integrate Dub with Python.
## 1. Prerequisites
To follow this guide, you will need to:
* [Create a Dub account](https://d.to/try)
* [Create an API key](https://app.dub.co/settings/tokens)
## 2. Install and initialize the Dub Python SDK
To install the [Dub Python SDK](https://dub.co/solutions/python), run the following command:
```bash pip
pip install dub
```
Initialize the Dub Python SDK by creating a new instance of the `Dub` class.
```python
import os
import dub
from dub.models import operations
d = dub.Dub(
token=os.environ['DUB_API_KEY'],
)
```
## 3. Create link
Let's create a short link using the [Dub Python SDK](https://dub.co/solutions/python).
```python index.py
res = d.links.create(request={
"url": "https://google.com",
})
print(res.short_link)
```
This will let you easily [update the link](#5-update-link) or [retrieve analytics](#6-retrieve-analytics-for-link) for it later on using the `externalId` instead of the Dub `linkId`.
The destination URL of the short link.
The domain of the short link. If not provided, the primary domain for the
workspace will be used (or `dub.sh` if the workspace has no domains).
The short link slug. If not provided, a random 7-character slug will be
generated.
This is the ID of the link in your database. If set, it can be used to
identify the link in the future.
The prefix of the short link slug for randomly-generated keys (e.g. if prefix
is `/c/`, generated keys will be in the `/c/:key` format). Will be ignored if
key is provided.
Whether the short link is archived.
Whether the short link's stats are publicly accessible.
The unique ID of the tag assigned to the short link. This field is deprecated
– use `tagIds` instead.
The unique IDs of the tags assigned to the short link.
The comments for the short link.
The date and time when the short link will expire at.
The URL to redirect to when the short link has expired.
The password required to access the destination URL of the short link.
Whether the short link uses Custom Social Media Cards feature.
The title of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
The description of the short link generated via `api.dub.co/metatags`. Will be
used for Custom Social Media Cards if proxy is true.
The image of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
Whether the short link uses link cloaking.
The iOS destination URL for the short link for iOS device targeting.
The Android destination URL for the short link for Android device targeting.
The UTM source of the short link. If set, this will populate or override the
UTM source in the destination URL.
The UTM medium of the short link. If set, this will populate or override the
UTM medium in the destination URL.
The UTM campaign of the short link. If set, this will populate or override the
UTM campaign in the destination URL.
The UTM term of the short link. If set, this will populate or override the UTM
term in the destination URL.
The UTM content of the short link. If set, this will populate or override the
UTM content in the destination URL.
Optionally, you can also pass an `externalId` field which is a unique identifier for the link in your own database to associate it with the link in Dub's system.
```python index.py
res = d.links.create(request={
"url": "https://google.com",
"external_id": "12345",
})
print(res.short_link)
```
## 4. Upsert link
Dub Python SDK provides a method to upsert a link – where an existing link is updated if it exists, or a new link is created if it doesn't. so you don't have to worry about checking if the link already exists.
```python index.py
res = d.links.upsert(request={
"url": "https://google.com",
})
print(res.short_link)
```
This way, you won't have to worry about checking if the link already exists when you're creating it.
## 5. Update link
Let's update an existing link using the Dub Python SDK.
You can do that in two ways:
* Using the link's `linkId` in Dub's system.
* Using the link's `externalId` in your own database (prefixed with `ext_`).
```python index.py
# Update a link by its linkId
res = d.links.update(link_id="clx1gvi9o0005hf5momm6f7hj", request_body={
"url": "https://google.uk",
})
print(res.short_link)
# Update a link by its externalId
res = d.links.update(external_id="ext_12345", request_body={
"url": "https://google.uk",
})
print(res.short_link)
```
## 6. Retrieve analytics for link
Dub allows you to retrieve analytics for a link using the Dub Python SDK.
```python index.py
# Retrieve the timeseries analytics for the last 7 days for a link
res = d.analytics.retrieve(request={
"link_id": "clx1gvi9o0005hf5momm6f7hj",
"interval": "7d",
"group_by": "timeseries",
})
print(res)
```
Similarly, you can retrieve analytics for a link using the `externalId` field.
```python index.py
# Retrieve the timeseries analytics for the last 7 days for a link
res = d.analytics.retrieve(request={
"external_id": "ext_12345",
"interval": "7d",
"group_by": "timeseries",
})
print(res)
```
## 7. Examples
See the full example on GitHub.
# Ruby on Rails
Learn how to integrate Dub with Ruby on Rails.
## 1. Prerequisites
To follow this guide, you will need to:
* [Create a Dub account](https://d.to/try)
* [Create an API key](https://app.dub.co/settings/tokens)
## 2. Install and initialize the Dub Ruby SDK
To install the [Dub Ruby SDK](https://dub.co/solutions/ruby), run the following command:
```bash bash
gem install dub
```
Initialize the Dub Ruby SDK by creating a new instance of the `Dub` struct.
```ruby
class LinksController < ApplicationController
require 'dub'
before_action :initialize_dub
def initialize_dub
@dub = ::OpenApiSDK::Dub.new
@dub.config_security(
::OpenApiSDK::Shared::Security.new(
token: ENV['DUB_API_KEY']
)
)
end
end
```
## 3. Create link
Let's create a short link using the [Dub Ruby SDK](https://dub.co/solutions/ruby).
```ruby links_controller.rb
def create
req = ::OpenApiSDK::Operations::CreateLinkRequest.new(
request_body: ::OpenApiSDK::Operations::CreateLinkRequestBody.new(
url: 'https://google.com'
)
)
res = @dub.links.create(req)
render json: res.raw_response.body
end
```
The destination URL of the short link.
The domain of the short link. If not provided, the primary domain for the
workspace will be used (or `dub.sh` if the workspace has no domains).
The short link slug. If not provided, a random 7-character slug will be
generated.
This is the ID of the link in your database. If set, it can be used to
identify the link in the future.
The prefix of the short link slug for randomly-generated keys (e.g. if prefix
is `/c/`, generated keys will be in the `/c/:key` format). Will be ignored if
key is provided.
Whether the short link is archived.
Whether the short link's stats are publicly accessible.
The unique ID of the tag assigned to the short link. This field is deprecated
– use `tagIds` instead.
The unique IDs of the tags assigned to the short link.
The comments for the short link.
The date and time when the short link will expire at.
The URL to redirect to when the short link has expired.
The password required to access the destination URL of the short link.
Whether the short link uses Custom Social Media Cards feature.
The title of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
The description of the short link generated via `api.dub.co/metatags`. Will be
used for Custom Social Media Cards if proxy is true.
The image of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
Whether the short link uses link cloaking.
The iOS destination URL for the short link for iOS device targeting.
The Android destination URL for the short link for Android device targeting.
The UTM source of the short link. If set, this will populate or override the
UTM source in the destination URL.
The UTM medium of the short link. If set, this will populate or override the
UTM medium in the destination URL.
The UTM campaign of the short link. If set, this will populate or override the
UTM campaign in the destination URL.
The UTM term of the short link. If set, this will populate or override the UTM
term in the destination URL.
The UTM content of the short link. If set, this will populate or override the
UTM content in the destination URL.
Optionally, you can also pass an `externalId` field which is a unique identifier for the link in your own database to associate it with the link in Dub's system.
```ruby links_controller.rb
def create
req = ::OpenApiSDK::Operations::CreateLinkRequest.new(
request_body: ::OpenApiSDK::Operations::CreateLinkRequestBody.new(
url: 'https://google.com',
external_id: '12345'
)
)
res = @dub.links.create(req)
render json: res.raw_response.body
end
```
This will let you easily [update the link](#5-update-link) or [retrieve analytics](#6-retrieve-analytics-for-link) for it later on using the `externalId` instead of the Dub `linkId`.
## 4. Upsert link
Dub Ruby SDK provides a method to upsert a link – where an existing link is updated if it exists, or a new link is created if it doesn't. so you don't have to worry about checking if the link already exists.
```ruby links_controller.rb
def upsert
req = ::OpenApiSDK::Operations::UpsertLinkRequest.new(
request_body: ::OpenApiSDK::Operations::UpsertLinkRequestBody.new(
url: "https://google.com",
),
)
res = @dub.links.upsert(req)
render json: res.raw_response.body
end
```
This way, you won't have to worry about checking if the link already exists when you're creating it.
## 5. Update link
Let's update an existing link using the Dub Ruby SDK.
You can do that in two ways:
* Using the link's `linkId` in Dub's system.
* Using the link's `externalId` in your own database (prefixed with `ext_`).
```ruby links_controller.rb
def update
req = ::OpenApiSDK::Operations::UpdateLinkRequest.new(
link_id: 'clx1gvi9o0005hf5momm6f7hj',
request_body: ::OpenApiSDK::Operations::UpdateLinkRequestBody.new(
url: 'https://google.uk'
)
)
res = @dub.links.update(req)
render json: res.raw_response.body
end
```
## 6. Retrieve analytics for link
Let's retrieve analytics for a link using the Dub Ruby SDK.
```ruby index.rb
def analytics
req = ::OpenApiSDK::Operations::RetrieveAnalyticsRequest.new(
link_id: "clx1gvi9o0005hf5momm6f7hj",
interval: ::OpenApiSDK::Operations::Interval::SEVEND,
group_by: ::OpenApiSDK::Operations::GroupBy::TIMESERIES
)
res = @dub.analytics.retrieve(req)
render json: res.raw_response.body
end
```
## 7. Examples
See the full example on GitHub.
# Remix
Learn how to integrate Dub with Remix.
## 1. Prerequisites
To follow this guide, you will need to:
* [Create a Dub account](https://d.to/try)
* [Create an API key](https://app.dub.co/settings/tokens)
## 2. Install and initialize the Dub TypeScript SDK
Install the [Dub TypeScript SDK](/sdks/typescript/overview) using your preferred package manager:
```bash
npm install dub
```
```bash
yarn add dub zod # zod is a peer dependency
```
```bash
pnpm add dub
```
Then, initialize the Dub TypeScript SDK with your API key.
```typescript lib/dub.ts
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY, // optional, defaults to DUB_API_KEY env variable
});
```
You can now use the `dub` object to interact with the Dub API.
```typescript
import { dub } from "./lib/dub";
```
## 3. Create link
Let's create a short link using the [Dub TypeScript SDK](/sdks/typescript/overview).
```typescript app/routes/create-link.ts
export const loader = async () => {
try {
const result = await dub.links.create({
url: "https://www.google.com",
});
return json(result, 200);
} catch (error: any) {
console.error(error);
return json(error, 400);
}
};
```
The destination URL of the short link.
The domain of the short link. If not provided, the primary domain for the
workspace will be used (or `dub.sh` if the workspace has no domains).
The short link slug. If not provided, a random 7-character slug will be
generated.
This is the ID of the link in your database. If set, it can be used to
identify the link in the future.
The prefix of the short link slug for randomly-generated keys (e.g. if prefix
is `/c/`, generated keys will be in the `/c/:key` format). Will be ignored if
key is provided.
Whether the short link is archived.
Whether the short link's stats are publicly accessible.
The unique ID of the tag assigned to the short link. This field is deprecated
– use `tagIds` instead.
The unique IDs of the tags assigned to the short link.
The comments for the short link.
The date and time when the short link will expire at.
The URL to redirect to when the short link has expired.
The password required to access the destination URL of the short link.
Whether the short link uses Custom Social Media Cards feature.
The title of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
The description of the short link generated via `api.dub.co/metatags`. Will be
used for Custom Social Media Cards if proxy is true.
The image of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
Whether the short link uses link cloaking.
The iOS destination URL for the short link for iOS device targeting.
The Android destination URL for the short link for Android device targeting.
The UTM source of the short link. If set, this will populate or override the
UTM source in the destination URL.
The UTM medium of the short link. If set, this will populate or override the
UTM medium in the destination URL.
The UTM campaign of the short link. If set, this will populate or override the
UTM campaign in the destination URL.
The UTM term of the short link. If set, this will populate or override the UTM
term in the destination URL.
The UTM content of the short link. If set, this will populate or override the
UTM content in the destination URL.
Optionally, you can also pass an `externalId` field which is a unique identifier for the link in your own database to associate it with the link in Dub's system.
```typescript app/routes/create-link.ts
export const loader = async () => {
try {
const result = await dub.links.create({
url: "https://www.google.com",
externalId: "12345",
});
return json(result, 200);
} catch (error: any) {
console.error(error);
return json(error, 400);
}
};
```
This will let you easily [update the link](#5-update-link) or [retrieve analytics](#6-retrieve-analytics-for-link) for it later on using the `externalId` instead of the Dub `linkId`.
## 4. Upsert link
Dub TypeScript SDK provides a method to upsert a link – where an existing link is updated if it exists, or a new link is created if it doesn't. so you don't have to worry about checking if the link already exists.
```typescript app/routes/upsert-link.ts
export const loader = async () => {
try {
const result = await dub.links.upsert({
url: "https://www.google.com",
});
return json(result, 200);
} catch (error: any) {
console.error(error);
return json(error, 400);
}
};
```
This way, you won't have to worry about checking if the link already exists when you're creating it.
## 5. Update link
Let's update an existing link using the Dub TypeScript SDK.
You can do that in two ways:
* Using the link's `linkId` in Dub's system.
* Using the link's `externalId` in your own database (prefixed with `ext_`).
```typescript app/routes/update-link.ts
export const loader = async () => {
try {
// Update a link by its linkId
const { shortLink } = await dub.links.update("clv3o9p9q000au1h0mc7r6l63", {
url: "https://www.google.uk", // new URL
});
// Update a link by its externalId
const { shortLink } = await dub.links.update("ext_12345", {
url: "https://www.google.uk", // new URL
});
return json({ shortLink }, 200);
} catch (error: any) {
console.error(error);
return json(error, 400);
}
};
```
## 6. Retrieve analytics for link
Dub allows you to retrieve analytics for a link using the Dub TypeScript SDK.
```typescript app/routes/analytics.ts
export const loader = async () => {
try {
// Retrieve the timeseries analytics for the last 7 days for a link
const result = await dub.analytics.retrieve({
linkId: "clv3o9p9q000au1h0mc7r6l63",
interval: "7d",
groupBy: "timeseries",
});
const timeseries = response as ClicksTimeseries[];
return json(timeseries, 200);
} catch (error: any) {
console.error(error);
return json(error, 400);
}
};
```
Similarly, you can retrieve analytics for a link using the `externalId` field.
```typescript app/routes/analytics.ts
// Retrieve the timeseries analytics for the last 7 days for a link
const response = await dub.analytics.retrieve({
externalId: "ext_12345",
interval: "7d",
groupBy: "timeseries",
});
const timeseries = response as ClicksTimeseries[];
```
## 7. Examples
See the full example on GitHub.
# Introduction
Learn how to integrate Dub with Ruby.
## 1. Prerequisites
To follow this guide, you will need to:
* [Create a Dub account](https://d.to/try)
* [Create an API key](https://app.dub.co/settings/tokens)
## 2. Install and initialize the Dub Ruby SDK
To install the [Dub Ruby SDK](https://dub.co/solutions/ruby), run the following command:
```bash bash
gem install dub
```
Initialize the Dub Ruby SDK by creating a new instance of the `Dub` struct.
```ruby
require 'dub'
dub = ::OpenApiSDK::Dub.new
dub.config_security(
::OpenApiSDK::Shared::Security.new(
token: ENV['DUB_API_KEY'],
)
)
```
## 3. Create link
Let's create a short link using the [Dub Ruby SDK](https://dub.co/solutions/ruby).
```ruby index.rb
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
```
The destination URL of the short link.
The domain of the short link. If not provided, the primary domain for the
workspace will be used (or `dub.sh` if the workspace has no domains).
The short link slug. If not provided, a random 7-character slug will be
generated.
This is the ID of the link in your database. If set, it can be used to
identify the link in the future.
The prefix of the short link slug for randomly-generated keys (e.g. if prefix
is `/c/`, generated keys will be in the `/c/:key` format). Will be ignored if
key is provided.
Whether the short link is archived.
Whether the short link's stats are publicly accessible.
The unique ID of the tag assigned to the short link. This field is deprecated
– use `tagIds` instead.
The unique IDs of the tags assigned to the short link.
The comments for the short link.
The date and time when the short link will expire at.
The URL to redirect to when the short link has expired.
The password required to access the destination URL of the short link.
Whether the short link uses Custom Social Media Cards feature.
The title of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
The description of the short link generated via `api.dub.co/metatags`. Will be
used for Custom Social Media Cards if proxy is true.
The image of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
Whether the short link uses link cloaking.
The iOS destination URL for the short link for iOS device targeting.
The Android destination URL for the short link for Android device targeting.
The UTM source of the short link. If set, this will populate or override the
UTM source in the destination URL.
The UTM medium of the short link. If set, this will populate or override the
UTM medium in the destination URL.
The UTM campaign of the short link. If set, this will populate or override the
UTM campaign in the destination URL.
The UTM term of the short link. If set, this will populate or override the UTM
term in the destination URL.
The UTM content of the short link. If set, this will populate or override the
UTM content in the destination URL.
Optionally, you can also pass an `externalId` field which is a unique identifier for the link in your own database to associate it with the link in Dub's system.
```ruby index.rb
req = ::OpenApiSDK::Operations::CreateLinkRequest.new(
request_body: ::OpenApiSDK::Operations::CreateLinkRequestBody.new(
url: "https://google.com",
external_id: "12345"
)
)
res = dub.links.create(req)
puts res.raw_response.body
```
This will let you easily [update the link](#5-update-link) or [retrieve analytics](#6-retrieve-analytics-for-link) for it later on using the `externalId` instead of the Dub `linkId`.
## 4. Upsert link
Dub Ruby SDK provides a method to upsert a link – where an existing link is updated if it exists, or a new link is created if it doesn't. so you don't have to worry about checking if the link already exists.
```ruby index.rb
req = ::OpenApiSDK::Operations::UpsertLinkRequest.new(
request_body: ::OpenApiSDK::Operations::UpsertLinkRequestBody.new(
url: "https://google.com",
),
)
res = dub.links.upsert(req)
puts res.raw_response.body
```
This way, you won't have to worry about checking if the link already exists when you're creating it.
## 5. Update link
Let's update an existing link using the Dub Ruby SDK.
You can do that in two ways:
* Using the link's `linkId` in Dub's system.
* Using the link's `externalId` in your own database (prefixed with `ext_`).
```ruby index.rb
req = ::OpenApiSDK::Operations::UpdateLinkRequest.new(
link_id: "cly2p8onm000cym8200nfay7l",
request_body: ::OpenApiSDK::Operations::UpdateLinkRequestBody.new(
url: "https://google.us",
),
)
res = dub.links.update(req)
puts res.raw_response.body
```
## 6. Retrieve analytics for link
Let's retrieve analytics for a link using the Dub Ruby SDK.
```ruby index.rb
req = ::OpenApiSDK::Operations::RetrieveAnalyticsRequest.new(
link_id: "clmnr6jcc0005l308q9v56uz1",
interval: ::OpenApiSDK::Operations::Interval::SEVEND,
group_by: ::OpenApiSDK::Operations::GroupBy::TIMESERIES
)
res = dub.analytics.retrieve(req)
puts res.raw_response.body
```
## 7. Examples
See the full example on GitHub.
# Sinatra
Learn how to integrate Dub with Sinatra.
## 1. Prerequisites
To follow this guide, you will need to:
* [Create a Dub account](https://d.to/try)
* [Create an API key](https://app.dub.co/settings/tokens)
## 2. Install and initialize the Dub Ruby SDK
To install the [Dub Ruby SDK](https://dub.co/solutions/ruby), run the following command:
```bash bash
gem install dub
```
Initialize the Dub Ruby SDK by creating a new instance of the `Dub` struct.
```ruby index.rb
require "sinatra"
require "dub"
dub = ::OpenApiSDK::Dub.new
dub.config_security(
::OpenApiSDK::Shared::Security.new(
token: ENV['DUB_API_KEY'],
)
)
```
## 3. Create link
Let's create a short link using the [Dub Ruby SDK](https://dub.co/solutions/ruby).
```ruby index.rb
post "/links" do
req = ::OpenApiSDK::Operations::CreateLinkRequest.new(
request_body: ::OpenApiSDK::Operations::CreateLinkRequestBody.new(
url: 'https://google.com'
)
)
res = dub.links.create(req)
content_type :json
res.raw_response.body
end
```
The destination URL of the short link.
The domain of the short link. If not provided, the primary domain for the
workspace will be used (or `dub.sh` if the workspace has no domains).
The short link slug. If not provided, a random 7-character slug will be
generated.
This is the ID of the link in your database. If set, it can be used to
identify the link in the future.
The prefix of the short link slug for randomly-generated keys (e.g. if prefix
is `/c/`, generated keys will be in the `/c/:key` format). Will be ignored if
key is provided.
Whether the short link is archived.
Whether the short link's stats are publicly accessible.
The unique ID of the tag assigned to the short link. This field is deprecated
– use `tagIds` instead.
The unique IDs of the tags assigned to the short link.
The comments for the short link.
The date and time when the short link will expire at.
The URL to redirect to when the short link has expired.
The password required to access the destination URL of the short link.
Whether the short link uses Custom Social Media Cards feature.
The title of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
The description of the short link generated via `api.dub.co/metatags`. Will be
used for Custom Social Media Cards if proxy is true.
The image of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
Whether the short link uses link cloaking.
The iOS destination URL for the short link for iOS device targeting.
The Android destination URL for the short link for Android device targeting.
The UTM source of the short link. If set, this will populate or override the
UTM source in the destination URL.
The UTM medium of the short link. If set, this will populate or override the
UTM medium in the destination URL.
The UTM campaign of the short link. If set, this will populate or override the
UTM campaign in the destination URL.
The UTM term of the short link. If set, this will populate or override the UTM
term in the destination URL.
The UTM content of the short link. If set, this will populate or override the
UTM content in the destination URL.
Optionally, you can also pass an `externalId` field which is a unique identifier for the link in your own database to associate it with the link in Dub's system.
```ruby index.rb
post "/links" do
req = ::OpenApiSDK::Operations::CreateLinkRequest.new(
request_body: ::OpenApiSDK::Operations::CreateLinkRequestBody.new(
url: 'https://google.com',
external_id: '12345'
)
)
res = dub.links.create(req)
content_type :json
res.raw_response.body
end
```
This will let you easily [update the link](#5-update-link) or [retrieve analytics](#6-retrieve-analytics-for-link) for it later on using the `externalId` instead of the Dub `linkId`.
## 4. Upsert link
Dub Ruby SDK provides a method to upsert a link – where an existing link is updated if it exists, or a new link is created if it doesn't. so you don't have to worry about checking if the link already exists.
```ruby index.rb
put "/links" do
req = ::OpenApiSDK::Operations::UpsertLinkRequest.new(
request_body: ::OpenApiSDK::Operations::UpsertLinkRequestBody.new(
url: "https://google.com",
),
)
res = dub.links.upsert(req)
content_type :json
res.raw_response.body
end
```
This way, you won't have to worry about checking if the link already exists when you're creating it.
## 5. Update link
Let's update an existing link using the Dub Ruby SDK.
You can do that in two ways:
* Using the link's `linkId` in Dub's system.
* Using the link's `externalId` in your own database (prefixed with `ext_`).
```ruby index.rb
patch "/links" do
req = ::OpenApiSDK::Operations::UpdateLinkRequest.new(
link_id: "clyci5h0w000511sjmu0tyjv9",
request_body: ::OpenApiSDK::Operations::UpdateLinkRequestBody.new(
url: 'https://google.uk'
)
)
res = dub.links.update(req)
content_type :json
res.raw_response.body
end
```
## 6. Retrieve analytics for link
Let's retrieve analytics for a link using the Dub Ruby SDK.
```ruby index.rb
get "/analytics" do
req = ::OpenApiSDK::Operations::RetrieveAnalyticsRequest.new(
link_id: "clx1gvi9o0005hf5momm6f7hj",
interval: ::OpenApiSDK::Operations::Interval::SEVEND,
group_by: ::OpenApiSDK::Operations::GroupBy::TIMESERIES
)
res = dub.analytics.retrieve(req)
content_type :json
res.raw_response.body
end
```
## 7. Examples
See the full example on GitHub.
# Introduction
Learn how to integrate Dub with TypeScript.
## 1. Prerequisites
To follow this guide, you will need to:
* [Create a Dub account](https://d.to/try)
* [Create an API key](https://app.dub.co/settings/tokens)
## 2. Install and initialize the Dub TypeScript SDK
Install the [Dub TypeScript SDK](/sdks/typescript/overview) using your preferred package manager:
```bash
npm install dub
```
```bash
yarn add dub zod # zod is a peer dependency
```
```bash
pnpm add dub
```
Then, initialize the Dub TypeScript SDK with your API key.
```typescript lib/dub.ts
import { Dub } from "dub";
export const dub = new Dub({
token: process.env.DUB_API_KEY, // optional, defaults to DUB_API_KEY env variable
});
```
You can now use the `dub` object to interact with the Dub API.
```typescript
import { dub } from "./lib/dub";
```
## 3. Create link
Let's create a short link using the [Dub TypeScript SDK](/sdks/typescript/overview).
```typescript create-link.ts
const { shortLink } = await dub.links.create({
url: "https://google.com",
});
console.log(shortLink); // e.g. https://dub.sh/abc123
```
The destination URL of the short link.
The domain of the short link. If not provided, the primary domain for the
workspace will be used (or `dub.sh` if the workspace has no domains).
The short link slug. If not provided, a random 7-character slug will be
generated.
This is the ID of the link in your database. If set, it can be used to
identify the link in the future.
The prefix of the short link slug for randomly-generated keys (e.g. if prefix
is `/c/`, generated keys will be in the `/c/:key` format). Will be ignored if
key is provided.
Whether the short link is archived.
Whether the short link's stats are publicly accessible.
The unique ID of the tag assigned to the short link. This field is deprecated
– use `tagIds` instead.
The unique IDs of the tags assigned to the short link.
The comments for the short link.
The date and time when the short link will expire at.
The URL to redirect to when the short link has expired.
The password required to access the destination URL of the short link.
Whether the short link uses Custom Social Media Cards feature.
The title of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
The description of the short link generated via `api.dub.co/metatags`. Will be
used for Custom Social Media Cards if proxy is true.
The image of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
Whether the short link uses link cloaking.
The iOS destination URL for the short link for iOS device targeting.
The Android destination URL for the short link for Android device targeting.
The UTM source of the short link. If set, this will populate or override the
UTM source in the destination URL.
The UTM medium of the short link. If set, this will populate or override the
UTM medium in the destination URL.
The UTM campaign of the short link. If set, this will populate or override the
UTM campaign in the destination URL.
The UTM term of the short link. If set, this will populate or override the UTM
term in the destination URL.
The UTM content of the short link. If set, this will populate or override the
UTM content in the destination URL.
Optionally, you can also pass an `externalId` field which is a unique identifier for the link in your own database to associate it with the link in Dub's system.
```typescript create-link-external-id.ts
await dub.links.create({
url: "https://google.com",
externalId: "12345",
});
```
This will let you easily [update the link](#5-update-link) or [retrieve analytics](#6-retrieve-analytics-for-link) for it later on using the `externalId` instead of the Dub `linkId`.
## 4. Upsert link
Dub TypeScript SDK provides a method to upsert a link – where an existing link is updated if it exists, or a new link is created if it doesn't. so you don't have to worry about checking if the link already exists.
```typescript upsert-link.ts
const { shortLink } = await dub.links.upsert({
url: "https://www.google.com",
});
console.log(shortLink); // will always be the same short link
```
This way, you won't have to worry about checking if the link already exists when you're creating it.
## 5. Update link
Let's update an existing link using the Dub TypeScript SDK.
You can do that in two ways:
* Using the link's `linkId` in Dub's system.
* Using the link's `externalId` in your own database (prefixed with `ext_`).
```typescript update-link.ts
// Update a link by its linkId
await dub.links.update("clv3o9p9q000au1h0mc7r6l63", {
url: "https://www.google.uk", // new URL
});
// Update a link by its externalId
await dub.links.update("ext_12345", {
url: "https://www.google.uk", // new URL
});
```
## 6. Retrieve analytics for link
Dub allows you to retrieve analytics for a link using the Dub TypeScript SDK.
```typescript get-analytics.ts
import { ClicksTimeseries } from "dub/models/components";
// Retrieve the timeseries analytics for the last 7 days for a link
const response = await dub.analytics.retrieve({
linkId: "clv3o9p9q000au1h0mc7r6l63",
interval: "7d",
groupBy: "timeseries",
});
const timeseries = response as ClicksTimeseries[];
```
Similarly, you can retrieve analytics for a link using the `externalId` field.
```typescript get-analytics-external-id.ts
import { ClicksTimeseries } from "dub/models/components";
// Retrieve the timeseries analytics for the last 7 days for a link
const response = await dub.analytics.retrieve({
externalId: "ext_12345",
interval: "7d",
groupBy: "timeseries",
});
const timeseries = response as ClicksTimeseries[];
```
## 7. Examples
See the full example on GitHub.
# Overview
Dub CLI for easily shortening URLs
## Install
```bash
npm i -g dub-cli@latest
```
```bash
yarn global add dub-cli@latest
```
```bash
pnpm i -g dub-cli@latest
```
To update an existing installation, run the same command.
## Version
Check your installed version of Dub CLI:
```bash
dub --version
# => 0.0.12
```
## Commands
Below is a list of available commands in the Dub CLI:
Log into the Dub platform:
```bash
dub login
```
View your workspace credentials:
```bash
dub config
```
Configure a domain for URL shortening:
```bash
dub domains
```
Create a short link:
```bash
dub shorten
```
You can preemptively pass the URL and the generated short link key, or go through the CLI prompts.
```bash
dub shorten [url] [key]
```
Search for links in your Dub workspace:
```bash
dub links
```
Here are the available options:
| Option | Description |
| ----------------------- | ------------------------------------------- |
| `-s, --search ` | Search by name |
| `-l, --limit ` | Limit the number of results (default is 10) |
```bash
dub links [options]
```
Get help for a specific command:
```bash
dub help [command]
```
## Demo
Watch the Dub CLI in action:
# Framer
How to add Dub Analytics to your Framer site
With Dub Analytics, you can track leads and sales conversions on your website, enabling you to measure the effectiveness of your marketing campaigns.
You can add 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
```
# React
How to add Dub Analytics to your React or Next.js site
With Dub Analytics, you can track leads and sales 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}
);
}
```
## Concepts
You can pass the following props to the `` component to customize the tracking script:
The base URL for the Dub API. This is useful for setting up reverse proxies to
avoid adblockers.
The publishable API key to use for client-side click tracking. Get your
publishable API key from your [Dub workspace's token settings
page](https://app.dub.co/settings/tokens).
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: `/`
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.
## Examples
We also have some advanced examples on GitHub:
See the full example on GitHub.
# Webflow
How to add Dub Analytics to your Webflow site
With Dub Analytics, you can track leads and sales conversions on your website, enabling you to measure the effectiveness of your marketing campaigns.
You can add 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
```
# WordPress
How to add Dub Analytics to your WordPress site
With Dub Analytics, you can track leads and sales conversions on your website, enabling you to measure the effectiveness of your marketing campaigns.
You can add 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
```
# Introduction
Client-side JavaScript SDK for for Dub Conversions
The following guides show you how to add [Dub Analytics](https://github.com/dubinc/analytics) to your website.
With Dub Analytics, you can [track leads and sales conversions](/conversions/quickstart) on your website, enabling you to measure the effectiveness of your marketing campaigns.
Based on the framework or platform you're using, you can install the script in different ways:
Add Dub Analytics to your React or Next.js app.
Add Dub Analytics to your WordPress site.
Add Dub Analytics to your Webflow site.
Add Dub Analytics to your Framer site.
# Overview
Open-source client libraries for the Dub.co API
## Server-side SDKs
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
# dub.analytics.retrieve
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://d.to/pro) subscription or
higher.
## Arguments
The type of event to retrieve analytics for. Available options: `clicks`,
`leads`, and `sales`.
The parameter to group the analytics data points by. Defaults to 'count' if
undefined. Available options: `count`, `timeseries`, `countries`, `cities`,
`devices`, `browsers`, `os`, `referers`, `referer_urls`, `top_links`,
`top_urls`, and `trigger`.
The domain of the short link.
The short link slug.
The unique ID of the short link on Dub.
This is the ID of the link in the your database. Must be prefixed with 'ext\_'
when passed as a query parameter.
The interval to retrieve analytics for.
Available options are `1h`, `24h`, `7d`, `30d`, `90d`, `ytd`, `1y`, and `all`.
Takes precedence over `start` and `end`.
The start date to retrieve analytics for.
Cannot be earlier than September 22, 2022.
The start date to retrieve analytics for.
Cannot be in the future. If not provided, defaults to the current date.
The IANA time zone code for aligning timeseries granularity (e.g.
America/New\_York). Defaults to UTC.
The continent to retrieve analytics for.
Available options are `AF`, `AN`, `AS`, `EU`, `NA`, `OC`, and `SA`.
The country to retrieve analytics for.
Available options are `AF`, `AL`, `DZ`,
`AS`, `AD`, `AO`, `AI`, `AQ`, `AG`, `AR`, `AM`, `AW`, `AU`, `AT`, `AZ`, `BS`,
`BH`, `BD`, `BB`, `BY`, `BE`, `BZ`, `BJ`, `BM`, `BT`, `BO`, `BA`, `BW`, `BV`,
`BR`, `IO`, `BN`, `BG`, `BF`, `BI`, `KH`, `CM`, `CA`, `CV`, `KY`, `CF`, `TD`,
`CL`, `CN`, `CX`, `CC`, `CO`, `KM`, `CG`, `CD`, `CK`, `CR`, `CI`, `HR`, `CU`,
`CY`, `CZ`, `DK`, `DJ`, `DM`, `DO`, `EC`, `EG`, `SV`, `GQ`, `ER`, `EE`, `ET`,
`FK`, `FO`, `FJ`, `FI`, `FR`, `GF`, `PF`, `TF`, `GA`, `GM`, `GE`, `DE`, `GH`,
`GI`, `GR`, `GL`, `GD`, `GP`, `GU`, `GT`, `GN`, `GW`, `GY`, `HT`, `HM`, `VA`,
`HN`, `HK`, `HU`, `IS`, `IN`, `ID`, `IR`, `IQ`, `IE`, `IL`, `IT`, `JM`, `JP`,
`JO`, `KZ`, `KE`, `KI`, `KP`, `KR`, `KW`, `KG`, `LA`, `LV`, `LB`, `LS`, `LR`,
`LY`, `LI`, `LT`, `LU`, `MO`, `MG`, `MW`, `MY`, `MV`, `ML`, `MT`, `MH`, `MQ`,
`MR`, `MU`, `YT`, `MX`, `FM`, `MD`, `MC`, `MN`, `MS`, `MA`, `MZ`, `MM`, `NA`,
`NR`, `NP`, `NL`, `NC`, `NZ`, `NI`, `NE`, `NG`, `NU`, `NF`, `MK`, `MP`, `NO`,
`OM`, `PK`, `PW`, `PS`, `PA`, `PG`, `PY`, `PE`, `PH`, `PN`, `PL`, `PT`, `PR`,
`QA`, `RE`, `RO`, `RU`, `RW`, `SH`, `KN`, `LC`, `PM`, `VC`, `WS`, `SM`, `ST`,
`SA`, `SN`, `SC`, `SL`, `SG`, `SK`, `SI`, `SB`, `SO`, `ZA`, `GS`, `ES`, `LK`,
`SD`, `SR`, `SJ`, `SZ`, `SE`, `CH`, `SY`, `TW`, `TJ`, `TZ`, `TH`, `TL`, `TG`,
`TK`, `TO`, `TT`, `TN`, `TR`, `TM`, `TC`, `TV`, `UG`, `UA`, `AE`, `GB`, `US`,
`UM`, `UY`, `UZ`, `VU`, `VE`, `VN`, `VG`, `VI`, `WF`, `EH`, `YE`, `ZM`, `ZW`,
`AX`, `BQ`, `CW`, `GG`, `IM`, `JE`, `ME`, `BL`, `MF`, `RS`, `SX`, `SS`, `XK`
The city to retrieve analytics for.
The device to retrieve analytics for.
The browser to retrieve analytics for.
The OS to retrieve analytics for.
The referer to retrieve analytics for.
The full referer URL to retrieve analytics for.
The URL to retrieve analytics for.
The tag ID to retrieve analytics for.
Filter for QR code scans. If true, filter for QR code scans only. If false,
filter for link clicks only. If undefined, return both.
Filter for root domains. If true, filter for domains only. If false, filter
for links only. If undefined, return both.
```typescript
// Retrieve total clicks for a link
await dub.analytics.retrieve({
event: "clicks",
domain: "dub.sh",
key: "dummy",
groupBy: "count",
});
// Retrieve timeseries analytics for a link
await dub.analytics.retrieve({
event: "clicks",
linkId: "abc123",
start: "30 days ago", // we support natural language for start and end dates
end: "now",
groupBy: "timeseries",
});
// Retrieve analytics by external ID
await dub.analytics.retrieve({
event: "clicks",
externalId: "ext_123", // ID of the link in your database – must be prefixed with 'ext_'
groupBy: "referer_urls", // we support grouping by the top referer URLs
});
```
# dub.domains.create
Create a domain for the authenticated workspace.
## Arguments
Name of the domain. Eg. `acme.com`.
The type of redirect to use for this domain.
redirectrewrite
The page your users will get redirected to when they visit your domain.
Redirect users to a specific URL when any link under this domain has expired.
Whether to archive this domain. false will unarchive a previously archived
domain.
Provide context to your teammates in the link creation modal by showing them
an example of a link to be shortened.
## Response
The unique identifier of the domain.
The domain name.
Whether the domain is verified.
Whether the domain is the primary domain for the workspace.
Whether the domain is archived.
Provide context to your teammates in the link creation modal by showing them
an example of a link to be shortened.
The URL to redirect to when a link under this domain has expired.
The page your users will get redirected to when they visit your domain.
The type of redirect to use for this domain. Available values are `redirect`
and `rewrite`.
The number of clicks on the domain.
```ts
await dub.domains.create({
slug: "acme.com",
});
```
```ts
{
id: "clvcep2sn87jh8nf808x00005",
slug: "acme.com",
verified: false,
primary: true,
archived: false,
placeholder: "https://dub.co/help/article/what-is-dub",
expiredUrl: null,
target: null,
type: "redirect",
clicks: 0
}
```
# dub.domains.delete
Delete a domain from a workspace. It cannot be undone. This will also delete all the links associated with the domain.
## Arguments
Name of the domain. Eg. `acme.com`.
## Response
Name of the domain that was deleted.
```ts
await dub.domains.delete("acme.com");
```
```ts
{
slug: "acme.com";
}
```
# dub.domains.list
Retrieve a list of domains associated with the authenticated workspace.
## Arguments
No arguments.
## Response
The unique identifier of the domain.
The domain name.
Whether the domain is verified.
Whether the domain is the primary domain for the workspace.
Whether the domain is archived.
Provide context to your teammates in the link creation modal by showing them
an example of a link to be shortened.
The URL to redirect to when a link under this domain has expired.
The page your users will get redirected to when they visit your domain.
The type of redirect to use for this domain. Available values are `redirect`
and `rewrite`.
The number of clicks on the domain.
```ts
const { result } = await dub.domains.list();
```
```ts
[
{
id: "clvcep2sn87jh8nf808x00005",
slug: "acme.com",
verified: false,
primary: true,
archived: false,
placeholder: "https://dub.co/help/article/what-is-dub",
expiredUrl: "https://acme.com/expired",
target: "https://acme.com/home",
type: "redirect",
clicks: 0,
},
];
```
# dub.domains.update
Edit a domain for the authenticated workspace.
## Arguments
Name of the domain. If provided, the existing domain will be updated with the
new domain. Eg. `acme.com`.
The type of redirect to use for this domain.
redirectrewrite
The page your users will get redirected to when they visit your domain.
Redirect users to a specific URL when any link under this domain has expired.
Whether to archive this domain. false will unarchive a previously archived
domain.
Provide context to your teammates in the link creation modal by showing them
an example of a link to be shortened.
## Response
The unique identifier of the domain.
The domain name.
Whether the domain is verified.
Whether the domain is the primary domain for the workspace.
Whether the domain is archived.
Provide context to your teammates in the link creation modal by showing them
an example of a link to be shortened.
The URL to redirect to when a link under this domain has expired.
The page your users will get redirected to when they visit your domain.
The type of redirect to use for this domain. Available values are `redirect`
and `rewrite`.
The number of clicks on the domain.
```ts
await dub.domains.update("acme.com", {
expiredUrl: "https://acme.com/expired",
target: "https://acme.com/home",
});
```
```ts
{
id: "clvcep2sn87jh8nf808x00005",
slug: "acme.com",
verified: false,
primary: true,
archived: false,
placeholder: "https://dub.co/help/article/what-is-dub",
expiredUrl: "https://acme.com/expired",
target: "https://acme.com/home",
type: "redirect",
clicks: 0
}
```
# dub.events.list
Retrieve a list of events for the authenticated workspace. The list will be paginated and the provided query parameters allow filtering the returned events.
Events endpoints require a [Business plan](https://d.to/business) subscription
or higher.
## Arguments
The type of event to retrieve events for. Available options: `clicks`,
`leads`, and `sales`.
The domain of the short link.
The short link slug.
The unique ID of the short link on Dub.
This is the ID of the link in the your database. Must be prefixed with 'ext\_'
when passed as a query parameter.
The interval to retrieve events for.
Available options are `1h`, `24h`, `7d`, `30d`, `90d`, `ytd`, `1y`, and `all`.
Takes precedence over `start` and `end`.
The start date to retrieve events for.
Cannot be earlier than September 22, 2022.
The start date to retrieve events for.
Cannot be in the future. If not provided, defaults to the current date.
The IANA time zone code for aligning timeseries granularity (e.g.
America/New\_York). Defaults to UTC.
The continent to retrieve events for.
Available options are `AF`, `AN`, `AS`, `EU`, `NA`, `OC`, and `SA`.
The country to retrieve events for.
Available options are `AF`, `AL`, `DZ`,
`AS`, `AD`, `AO`, `AI`, `AQ`, `AG`, `AR`, `AM`, `AW`, `AU`, `AT`, `AZ`, `BS`,
`BH`, `BD`, `BB`, `BY`, `BE`, `BZ`, `BJ`, `BM`, `BT`, `BO`, `BA`, `BW`, `BV`,
`BR`, `IO`, `BN`, `BG`, `BF`, `BI`, `KH`, `CM`, `CA`, `CV`, `KY`, `CF`, `TD`,
`CL`, `CN`, `CX`, `CC`, `CO`, `KM`, `CG`, `CD`, `CK`, `CR`, `CI`, `HR`, `CU`,
`CY`, `CZ`, `DK`, `DJ`, `DM`, `DO`, `EC`, `EG`, `SV`, `GQ`, `ER`, `EE`, `ET`,
`FK`, `FO`, `FJ`, `FI`, `FR`, `GF`, `PF`, `TF`, `GA`, `GM`, `GE`, `DE`, `GH`,
`GI`, `GR`, `GL`, `GD`, `GP`, `GU`, `GT`, `GN`, `GW`, `GY`, `HT`, `HM`, `VA`,
`HN`, `HK`, `HU`, `IS`, `IN`, `ID`, `IR`, `IQ`, `IE`, `IL`, `IT`, `JM`, `JP`,
`JO`, `KZ`, `KE`, `KI`, `KP`, `KR`, `KW`, `KG`, `LA`, `LV`, `LB`, `LS`, `LR`,
`LY`, `LI`, `LT`, `LU`, `MO`, `MG`, `MW`, `MY`, `MV`, `ML`, `MT`, `MH`, `MQ`,
`MR`, `MU`, `YT`, `MX`, `FM`, `MD`, `MC`, `MN`, `MS`, `MA`, `MZ`, `MM`, `NA`,
`NR`, `NP`, `NL`, `NC`, `NZ`, `NI`, `NE`, `NG`, `NU`, `NF`, `MK`, `MP`, `NO`,
`OM`, `PK`, `PW`, `PS`, `PA`, `PG`, `PY`, `PE`, `PH`, `PN`, `PL`, `PT`, `PR`,
`QA`, `RE`, `RO`, `RU`, `RW`, `SH`, `KN`, `LC`, `PM`, `VC`, `WS`, `SM`, `ST`,
`SA`, `SN`, `SC`, `SL`, `SG`, `SK`, `SI`, `SB`, `SO`, `ZA`, `GS`, `ES`, `LK`,
`SD`, `SR`, `SJ`, `SZ`, `SE`, `CH`, `SY`, `TW`, `TJ`, `TZ`, `TH`, `TL`, `TG`,
`TK`, `TO`, `TT`, `TN`, `TR`, `TM`, `TC`, `TV`, `UG`, `UA`, `AE`, `GB`, `US`,
`UM`, `UY`, `UZ`, `VU`, `VE`, `VN`, `VG`, `VI`, `WF`, `EH`, `YE`, `ZM`, `ZW`,
`AX`, `BQ`, `CW`, `GG`, `IM`, `JE`, `ME`, `BL`, `MF`, `RS`, `SX`, `SS`, `XK`
The city to retrieve events for.
The device to retrieve events for.
The browser to retrieve events for.
The OS to retrieve events for.
The referer to retrieve events for.
The full referer URL to retrieve events for.
The URL to retrieve events for.
The tag ID to retrieve events for.
Filter for QR code scans. If true, filter for QR code scans only. If false,
filter for link clicks only. If undefined, return both.
Filter for root domains. If true, filter for domains only. If false, filter
for links only. If undefined, return both.
The number of records to retrieve.
Number of records to offset the results by.
The field to sort the results by.
The order to sort the results by.
```ts
await dub.events.list({
event: "clicks",
linkId: "abc123",
start: "a week ago", // we support relative dates like "a week ago"
end: "today", // we support relative dates like "today"
});
```
# Count
Retrieve the number of links for the authenticated workspace. The provided query parameters allow filtering the returned links.
## Arguments
The domain to filter the links by. E.g. `ac.me`. If not provided, all links
for the workspace will be returned.
The tag ID to filter the links by. This field is deprecated – use `tagIds`
instead.
The tag IDs to filter the links by.
The search term to filter the links by. The search term will be matched
against the short link slug and the destination URL.
The user ID to filter the links by.
Whether to include archived links in the response. Defaults to `false` if not
provided.
Whether to include tags in the response. Defaults to `false` if not provided.
The field to group the links by. Available options are `domain` and `tagId`.
## Response
The number of links matching the query.
```ts
// Count links by domain
await dub.links.count({
domain: "dub.sh",
});
// Count links by tagIds
await dub.links.count({
tagIds: ["clv3mewk30001mq0rxl3j3frn"],
});
```
```ts
2;
```
# Create
Create a new link for the authenticated workspace.
## Arguments
The destination URL of the short link.
The domain of the short link. If not provided, the primary domain for the
workspace will be used (or `dub.sh` if the workspace has no domains).
The short link slug. If not provided, a random 7-character slug will be
generated.
This is the ID of the link in your database. If set, it can be used to
identify the link in the future.
The prefix of the short link slug for randomly-generated keys (e.g. if prefix
is `/c/`, generated keys will be in the `/c/:key` format). Will be ignored if
key is provided.
Whether the short link is archived.
Whether the short link's stats are publicly accessible.
The unique ID of the tag assigned to the short link. This field is deprecated
– use `tagIds` instead.
The unique IDs of the tags assigned to the short link.
The comments for the short link.
The date and time when the short link will expire at.
The URL to redirect to when the short link has expired.
The password required to access the destination URL of the short link.
Whether the short link uses Custom Social Media Cards feature.
The title of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
The description of the short link generated via `api.dub.co/metatags`. Will be
used for Custom Social Media Cards if proxy is true.
The image of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
Whether the short link uses link cloaking.
The iOS destination URL for the short link for iOS device targeting.
The Android destination URL for the short link for Android device targeting.
The UTM source of the short link. If set, this will populate or override the
UTM source in the destination URL.
The UTM medium of the short link. If set, this will populate or override the
UTM medium in the destination URL.
The UTM campaign of the short link. If set, this will populate or override the
UTM campaign in the destination URL.
The UTM term of the short link. If set, this will populate or override the UTM
term in the destination URL.
The UTM content of the short link. If set, this will populate or override the
UTM content in the destination URL.
## Response
The unique ID of the short link.
The domain of the short link. If not provided, the primary domain for the
workspace will be used (or dub.sh if the workspace has no domains).
The short link slug. If not provided, a random 7-character slug will be
generated.
This is the ID of the link in your database. If set, it can be used to
identify the link in the future. Must be prefixed with `ext_` when provided to
`links.get`, `links.update`, and `links.delete` methods.
The destination URL of the short link.
Whether the short link is archived.
The date and time when the short link will expire in ISO-8601 format.
The URL to redirect to when the short link has expired.
The password required to access the destination URL of the short link.
Whether the short link uses Custom Social Media Cards feature.
The title of the short link generated via api.dub.co/metatags. Will be used
for Custom Social Media Cards if proxy is true.
The description of the short link generated via api.dub.co/metatags. Will be
used for Custom Social Media Cards if proxy is true.
The image of the short link generated via api.dub.co/metatags. Will be used
for Custom Social Media Cards if proxy is true.
Whether the short link uses link cloaking.
The iOS destination URL for the short link for iOS device targeting.
The Android destination URL for the short link for Android device targeting.
Whether the short link's stats are publicly accessible.
The unique ID of the tag assigned to the short link. This field is deprecated
– use `tags` instead.
The tags assigned to the short link.
The unique ID of the tag.
The name of the tag.
The color of the tag.
The comments for the short link.
The full URL of the short link, including the https protocol (e.g.
`https://dub.sh/try`).
The full URL of the QR code for the short link (e.g.
`https://api.dub.co/qr?url=https://dub.sh/try`).
The UTM source of the short link.
The UTM medium of the short link.
The UTM campaign of the short link.
The UTM term of the short link.
The UTM content of the short link.
The user ID of the creator of the short link.
The workspace ID of the short link.
The number of clicks on the short link.
The date and time when the short link was last clicked.
The date and time when the short link was created.
The date and time when the short link was last updated.
The project ID of the short link. This field is deprecated – use `workspaceId`
instead.
```ts
await dub.links.create({
url: "https://www.google.com",
});
```
```ts
{
id: 'clv3o9p9q000au1h0mc7r6l63',
domain: 'dub.sh',
key: 'e7qzMuI',
externalId: "123",
url: 'https://www.google.com',
archived: false,
expiresAt: null,
expiredUrl: null,
password: null,
proxy: false,
title: null,
description: null,
image: null,
rewrite: false,
ios: null,
android: null,
geo: null,
publicStats: false,
tagId: null,
tags: [],
comments: null,
shortLink: 'https://dub.sh/e7qzMuI',
qrCode: 'https://api.dub.co/qr?url=https://dub.sh/e7qzMuI?qr=1',
utmSource: null,
utmMedium: null,
utmCampaign: null,
utmTerm: null,
utmContent: null,
userId: 'cludszk1h0000wmd2e0ea2b0p',
workspaceId: 'ws_clugls3tn000lwfotbpy30304',
clicks: 0,
lastClicked: null,
createdAt: '2024-04-17T10:31:00.398Z',
updatedAt: '2024-04-17T10:31:00.398Z',
projectId: 'clugls3tn000lwfotbpy30304'
}
```
# Create Many
Bulk create up to 100 links for the authenticated workspace.
## Arguments
The destination URL of the short link.
The domain of the short link. If not provided, the primary domain for the
workspace will be used (or `dub.sh` if the workspace has no domains).
The short link slug. If not provided, a random 7-character slug will be
generated.
This is the ID of the link in your database. If set, it can be used to
identify the link in the future.
The prefix of the short link slug for randomly-generated keys (e.g. if prefix
is `/c/`, generated keys will be in the `/c/:key` format). Will be ignored if
key is provided.
Whether the short link is archived.
Whether the short link's stats are publicly accessible.
The unique ID of the tag assigned to the short link. This field is deprecated
– use `tagIds` instead.
The unique IDs of the tags assigned to the short link.
The comments for the short link.
The date and time when the short link will expire at.
The URL to redirect to when the short link has expired.
The password required to access the destination URL of the short link.
Whether the short link uses Custom Social Media Cards feature.
The title of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
The description of the short link generated via `api.dub.co/metatags`. Will be
used for Custom Social Media Cards if proxy is true.
The image of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
Whether the short link uses link cloaking.
The iOS destination URL for the short link for iOS device targeting.
The Android destination URL for the short link for Android device targeting.
The UTM source of the short link. If set, this will populate or override the
UTM source in the destination URL.
The UTM medium of the short link. If set, this will populate or override the
UTM medium in the destination URL.
The UTM campaign of the short link. If set, this will populate or override the
UTM campaign in the destination URL.
The UTM term of the short link. If set, this will populate or override the UTM
term in the destination URL.
The UTM content of the short link. If set, this will populate or override the
UTM content in the destination URL.
## Response
The unique ID of the short link.
The domain of the short link. If not provided, the primary domain for the
workspace will be used (or dub.sh if the workspace has no domains).
The short link slug. If not provided, a random 7-character slug will be
generated.
This is the ID of the link in your database. If set, it can be used to
identify the link in the future. Must be prefixed with `ext_` when provided to
`links.get`, `links.update`, and `links.delete` methods.
The destination URL of the short link.
Whether the short link is archived.
The date and time when the short link will expire in ISO-8601 format.
The URL to redirect to when the short link has expired.
The password required to access the destination URL of the short link.
Whether the short link uses Custom Social Media Cards feature.
The title of the short link generated via api.dub.co/metatags. Will be used
for Custom Social Media Cards if proxy is true.
The description of the short link generated via api.dub.co/metatags. Will be
used for Custom Social Media Cards if proxy is true.
The image of the short link generated via api.dub.co/metatags. Will be used
for Custom Social Media Cards if proxy is true.
Whether the short link uses link cloaking.
The iOS destination URL for the short link for iOS device targeting.
The Android destination URL for the short link for Android device targeting.
Whether the short link's stats are publicly accessible.
The unique ID of the tag assigned to the short link. This field is deprecated
– use `tags` instead.
The tags assigned to the short link.
The unique ID of the tag.
The name of the tag.
The color of the tag.
The comments for the short link.
The full URL of the short link, including the https protocol (e.g.
`https://dub.sh/try`).
The full URL of the QR code for the short link (e.g.
`https://api.dub.co/qr?url=https://dub.sh/try`).
The UTM source of the short link.
The UTM medium of the short link.
The UTM campaign of the short link.
The UTM term of the short link.
The UTM content of the short link.
The user ID of the creator of the short link.
The workspace ID of the short link.
The number of clicks on the short link.
The date and time when the short link was last clicked.
The date and time when the short link was created.
The date and time when the short link was last updated.
The project ID of the short link. This field is deprecated – use `workspaceId`
instead.
```ts
await dub.links.createMany([
{
url: "https://www.google.com",
},
{
url: "https://github.com",
},
]);
```
```ts
[
{
id: "clv3u86ej0008y9nm1f1yto2w",
domain: "dub.sh",
key: "FTC1AXj",
externalId: "123",
url: "https://www.google.com",
archived: false,
expiresAt: null,
expiredUrl: null,
password: null,
proxy: false,
title: null,
description: null,
image: null,
rewrite: false,
ios: null,
android: null,
geo: null,
...
},
{
id: "clv3u86ej000ay9nm55qgbh5r",
domain: "dub.sh",
key: "B6qpMch",
externalId: "123",
url: "https://github.com",
archived: false,
expiresAt: null,
expiredUrl: null,
password: null,
proxy: false,
title: null,
description: null,
image: null,
rewrite: false,
ios: null,
android: null,
geo: null,
...
},
];
```
# Delete
Delete a link for the authenticated workspace.
## Arguments
The id of the link to delete. You can get this via the `links.get` method.
Alternatively, you can use your `externalId` prefixed with `ext_`.
## Response
The id of the link that was deleted.
```ts
// Delete a link by linkId
await dub.links.delete("clv3o9p9q000au1h0mc7r6l63");
// Delete a link by externalId
await dub.links.delete("ext_123");
```
```ts
{
id: "clv3o9p9q000au1h0mc7r6l63";
}
```
# Get
Retrieve info for a link by specifying either the domain and key or the linkId or externalId
## Arguments
The domain of the link to retrieve. E.g. for `d.to/github`, the domain is
`d.to`.
The key of the link to retrieve. E.g. for `d.to/github`, the key is `github`.
The unique ID of the short link.
This is the ID of the link in the your database. Must be prefixed with `ext_`
when provided.
## Response
The unique ID of the short link.
The domain of the short link. If not provided, the primary domain for the
workspace will be used (or dub.sh if the workspace has no domains).
The short link slug. If not provided, a random 7-character slug will be
generated.
This is the ID of the link in your database. If set, it can be used to
identify the link in the future. Must be prefixed with `ext_` when provided to
`links.get`, `links.update`, and `links.delete` methods.
The destination URL of the short link.
Whether the short link is archived.
The date and time when the short link will expire in ISO-8601 format.
The URL to redirect to when the short link has expired.
The password required to access the destination URL of the short link.
Whether the short link uses Custom Social Media Cards feature.
The title of the short link generated via api.dub.co/metatags. Will be used
for Custom Social Media Cards if proxy is true.
The description of the short link generated via api.dub.co/metatags. Will be
used for Custom Social Media Cards if proxy is true.
The image of the short link generated via api.dub.co/metatags. Will be used
for Custom Social Media Cards if proxy is true.
Whether the short link uses link cloaking.
The iOS destination URL for the short link for iOS device targeting.
The Android destination URL for the short link for Android device targeting.
Whether the short link's stats are publicly accessible.
The unique ID of the tag assigned to the short link. This field is deprecated
– use `tags` instead.
The tags assigned to the short link.
The unique ID of the tag.
The name of the tag.
The color of the tag.
The comments for the short link.
The full URL of the short link, including the https protocol (e.g.
`https://dub.sh/try`).
The full URL of the QR code for the short link (e.g.
`https://api.dub.co/qr?url=https://dub.sh/try`).
The UTM source of the short link.
The UTM medium of the short link.
The UTM campaign of the short link.
The UTM term of the short link.
The UTM content of the short link.
The user ID of the creator of the short link.
The workspace ID of the short link.
The number of clicks on the short link.
The date and time when the short link was last clicked.
The date and time when the short link was created.
The date and time when the short link was last updated.
The project ID of the short link. This field is deprecated – use `workspaceId`
instead.
```ts
// Retrieve a link by domain and key
await dub.links.get({
domain: "dub.sh",
key: "e7qzMuI",
});
// Retrieve a link by linkId
await dub.links.get({
linkId: "clv3o9p9q000au1h0mc7r6l63",
});
// Retrieve a link by externalId
await dub.links.get({
externalId: "ext_123",
});
```
```ts
{
id: 'clv3o9p9q000au1h0mc7r6l63',
domain: 'dub.sh',
key: 'e7qzMuI',
externalId: "123",
url: 'https://www.google.com',
archived: false,
expiresAt: null,
expiredUrl: null,
password: null,
proxy: false,
title: null,
description: null,
image: null,
rewrite: false,
ios: null,
android: null,
geo: null,
publicStats: false,
tagId: null,
tags: [],
comments: null,
shortLink: 'https://dub.sh/e7qzMuI',
qrCode: 'https://api.dub.co/qr?url=https://dub.sh/e7qzMuI?qr=1',
utmSource: null,
utmMedium: null,
utmCampaign: null,
utmTerm: null,
utmContent: null,
userId: 'cludszk1h0000wmd2e0ea2b0p',
workspaceId: 'ws_clugls3tn000lwfotbpy30304',
clicks: 0,
lastClicked: null,
createdAt: '2024-04-17T10:31:00.398Z',
updatedAt: '2024-04-17T10:31:00.398Z',
projectId: 'clugls3tn000lwfotbpy30304'
}
```
# List
Retrieve a list of links for the authenticated workspace. The list will be paginated and the provided query parameters allow filtering the returned links.
## Arguments
The domain to filter the links by. E.g. `ac.me`. If not provided, all links
for the workspace will be returned.
The tag ID to filter the links by. This field is deprecated – use `tagIds`
instead.
The tag IDs to filter the links by.
The search term to filter the links by. The search term will be matched
against the short link slug and the destination URL.
The user ID to filter the links by.
Whether to include archived links in the response. Defaults to `false` if not
provided.
Whether to include tags in the response. Defaults to `false` if not provided.
The field to sort the links by. The default is `createdAt`, and sort order is
always descending. Available options are `createdAt`, `clicks`, and
`lastClicked`.
The page number for pagination (each page contains `100` links).
## Response
The unique ID of the short link.
The domain of the short link. If not provided, the primary domain for the
workspace will be used (or dub.sh if the workspace has no domains).
The short link slug. If not provided, a random 7-character slug will be
generated.
This is the ID of the link in your database. If set, it can be used to
identify the link in the future. Must be prefixed with `ext_` when provided to
`links.get`, `links.update`, and `links.delete` methods.
The destination URL of the short link.
Whether the short link is archived.
The date and time when the short link will expire in ISO-8601 format.
The URL to redirect to when the short link has expired.
The password required to access the destination URL of the short link.
Whether the short link uses Custom Social Media Cards feature.
The title of the short link generated via api.dub.co/metatags. Will be used
for Custom Social Media Cards if proxy is true.
The description of the short link generated via api.dub.co/metatags. Will be
used for Custom Social Media Cards if proxy is true.
The image of the short link generated via api.dub.co/metatags. Will be used
for Custom Social Media Cards if proxy is true.
Whether the short link uses link cloaking.
The iOS destination URL for the short link for iOS device targeting.
The Android destination URL for the short link for Android device targeting.
Whether the short link's stats are publicly accessible.
The unique ID of the tag assigned to the short link. This field is deprecated
– use `tags` instead.
The tags assigned to the short link.
The unique ID of the tag.
The name of the tag.
The color of the tag.
The comments for the short link.
The full URL of the short link, including the https protocol (e.g.
`https://dub.sh/try`).
The full URL of the QR code for the short link (e.g.
`https://api.dub.co/qr?url=https://dub.sh/try`).
The UTM source of the short link.
The UTM medium of the short link.
The UTM campaign of the short link.
The UTM term of the short link.
The UTM content of the short link.
The user ID of the creator of the short link.
The workspace ID of the short link.
The number of clicks on the short link.
The date and time when the short link was last clicked.
The date and time when the short link was created.
The date and time when the short link was last updated.
The project ID of the short link. This field is deprecated – use `workspaceId`
instead.
```ts
// Find links by domain
const { result } = await dub.links.list({
domain: "dub.sh",
});
// Find links by tagIds
const { result } = await dub.links.list({
tagIds: ["clv3mewk30001mq0rxl3j3frn"],
});
```
```ts
[
{
id: "clv3o9p9q000au1h0mc7r6l63",
domain: "dub.sh",
key: "e7qzMuI",
externalId: "123",
url: "https://www.google.com",
archived: false,
expiresAt: null,
expiredUrl: null,
password: null,
proxy: false,
title: null,
description: null,
image: null,
rewrite: false,
ios: null,
android: null,
geo: null,
publicStats: false,
tagId: null,
tags: [],
comments: null,
shortLink: "https://dub.sh/e7qzMuI",
qrCode: "https://api.dub.co/qr?url=https://dub.sh/e7qzMuI?qr=1",
utmSource: null,
utmMedium: null,
utmCampaign: null,
utmTerm: null,
utmContent: null,
userId: "cludszk1h0000wmd2e0ea2b0p",
workspaceId: "ws_clugls3tn000lwfotbpy30304",
clicks: 0,
lastClicked: null,
createdAt: "2024-04-17T10:31:00.398Z",
updatedAt: "2024-04-17T10:31:00.398Z",
projectId: "clugls3tn000lwfotbpy30304",
},
];
```
# Update
Edit a link for the authenticated workspace. Support partial updates.
## Arguments
The destination URL of the short link.
The domain of the short link. If not provided, the primary domain for the
workspace will be used (or `dub.sh` if the workspace has no domains).
The short link slug. If not provided, a random 7-character slug will be
generated.
This is the ID of the link in your database. If set, it can be used to
identify the link in the future.
The prefix of the short link slug for randomly-generated keys (e.g. if prefix
is `/c/`, generated keys will be in the `/c/:key` format). Will be ignored if
key is provided.
Whether the short link is archived.
Whether the short link's stats are publicly accessible.
The unique ID of the tag assigned to the short link. This field is deprecated
– use `tagIds` instead.
The unique IDs of the tags assigned to the short link.
The comments for the short link.
The date and time when the short link will expire at.
The URL to redirect to when the short link has expired.
The password required to access the destination URL of the short link.
Whether the short link uses Custom Social Media Cards feature.
The title of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
The description of the short link generated via `api.dub.co/metatags`. Will be
used for Custom Social Media Cards if proxy is true.
The image of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
Whether the short link uses link cloaking.
The iOS destination URL for the short link for iOS device targeting.
The Android destination URL for the short link for Android device targeting.
The UTM source of the short link. If set, this will populate or override the
UTM source in the destination URL.
The UTM medium of the short link. If set, this will populate or override the
UTM medium in the destination URL.
The UTM campaign of the short link. If set, this will populate or override the
UTM campaign in the destination URL.
The UTM term of the short link. If set, this will populate or override the UTM
term in the destination URL.
The UTM content of the short link. If set, this will populate or override the
UTM content in the destination URL.
## Response
The unique ID of the short link.
The domain of the short link. If not provided, the primary domain for the
workspace will be used (or dub.sh if the workspace has no domains).
The short link slug. If not provided, a random 7-character slug will be
generated.
This is the ID of the link in your database. If set, it can be used to
identify the link in the future. Must be prefixed with `ext_` when provided to
`links.get`, `links.update`, and `links.delete` methods.
The destination URL of the short link.
Whether the short link is archived.
The date and time when the short link will expire in ISO-8601 format.
The URL to redirect to when the short link has expired.
The password required to access the destination URL of the short link.
Whether the short link uses Custom Social Media Cards feature.
The title of the short link generated via api.dub.co/metatags. Will be used
for Custom Social Media Cards if proxy is true.
The description of the short link generated via api.dub.co/metatags. Will be
used for Custom Social Media Cards if proxy is true.
The image of the short link generated via api.dub.co/metatags. Will be used
for Custom Social Media Cards if proxy is true.
Whether the short link uses link cloaking.
The iOS destination URL for the short link for iOS device targeting.
The Android destination URL for the short link for Android device targeting.
Whether the short link's stats are publicly accessible.
The unique ID of the tag assigned to the short link. This field is deprecated
– use `tags` instead.
The tags assigned to the short link.
The unique ID of the tag.
The name of the tag.
The color of the tag.
The comments for the short link.
The full URL of the short link, including the https protocol (e.g.
`https://dub.sh/try`).
The full URL of the QR code for the short link (e.g.
`https://api.dub.co/qr?url=https://dub.sh/try`).
The UTM source of the short link.
The UTM medium of the short link.
The UTM campaign of the short link.
The UTM term of the short link.
The UTM content of the short link.
The user ID of the creator of the short link.
The workspace ID of the short link.
The number of clicks on the short link.
The date and time when the short link was last clicked.
The date and time when the short link was created.
The date and time when the short link was last updated.
The project ID of the short link. This field is deprecated – use `workspaceId`
instead.
```ts
// Update a link by its linkId
await dub.links.update("clv3o9p9q000au1h0mc7r6l63", {
url: "https://www.google.com",
});
// Update a link by its externalId
await dub.links.update("ext_123", {
url: "https://www.google.com",
});
```
```ts
{
id: 'clv3o9p9q000au1h0mc7r6l63',
domain: 'dub.sh',
key: 'e7qzMuI',
externalId: "123",
url: 'https://www.google.com',
archived: false,
expiresAt: null,
expiredUrl: null,
password: null,
proxy: false,
title: null,
description: null,
image: null,
rewrite: false,
ios: null,
android: null,
geo: null,
publicStats: false,
tagId: 'clv3j1h7o00032u1ngo8luv84',
tags: [
{
id: 'clv3j1h7o00032u1ngo8luv84',
name: 'news',
color: 'blue'
}
],
comments: null,
shortLink: 'https://dub.sh/e7qzMuI',
qrCode: 'https://api.dub.co/qr?url=https://dub.sh/e7qzMuI?qr=1',
utmSource: null,
utmMedium: null,
utmCampaign: null,
utmTerm: null,
utmContent: null,
userId: 'cludszk1h0000wmd2e0ea2b0p',
workspaceId: 'ws_clugls3tn000lwfotbpy30304',
clicks: 0,
lastClicked: null,
createdAt: '2024-04-17T10:31:00.398Z',
updatedAt: '2024-04-17T10:31:00.398Z',
projectId: 'clugls3tn000lwfotbpy30304'
}
```
# 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.
## Arguments
The destination URL of the short link.
The domain of the short link. If not provided, the primary domain for the
workspace will be used (or `dub.sh` if the workspace has no domains).
The short link slug. If not provided, a random 7-character slug will be
generated.
This is the ID of the link in your database. If set, it can be used to
identify the link in the future.
The prefix of the short link slug for randomly-generated keys (e.g. if prefix
is `/c/`, generated keys will be in the `/c/:key` format). Will be ignored if
key is provided.
Whether the short link is archived.
Whether the short link's stats are publicly accessible.
The unique ID of the tag assigned to the short link. This field is deprecated
– use `tagIds` instead.
The unique IDs of the tags assigned to the short link.
The comments for the short link.
The date and time when the short link will expire at.
The URL to redirect to when the short link has expired.
The password required to access the destination URL of the short link.
Whether the short link uses Custom Social Media Cards feature.
The title of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
The description of the short link generated via `api.dub.co/metatags`. Will be
used for Custom Social Media Cards if proxy is true.
The image of the short link generated via `api.dub.co/metatags`. Will be used
for Custom Social Media Cards if proxy is true.
Whether the short link uses link cloaking.
The iOS destination URL for the short link for iOS device targeting.
The Android destination URL for the short link for Android device targeting.
The UTM source of the short link. If set, this will populate or override the
UTM source in the destination URL.
The UTM medium of the short link. If set, this will populate or override the
UTM medium in the destination URL.
The UTM campaign of the short link. If set, this will populate or override the
UTM campaign in the destination URL.
The UTM term of the short link. If set, this will populate or override the UTM
term in the destination URL.
The UTM content of the short link. If set, this will populate or override the
UTM content in the destination URL.
## Response
The unique ID of the short link.
The domain of the short link. If not provided, the primary domain for the
workspace will be used (or dub.sh if the workspace has no domains).
The short link slug. If not provided, a random 7-character slug will be
generated.
This is the ID of the link in your database. If set, it can be used to
identify the link in the future. Must be prefixed with `ext_` when provided to
`links.get`, `links.update`, and `links.delete` methods.
The destination URL of the short link.
Whether the short link is archived.
The date and time when the short link will expire in ISO-8601 format.
The URL to redirect to when the short link has expired.
The password required to access the destination URL of the short link.
Whether the short link uses Custom Social Media Cards feature.
The title of the short link generated via api.dub.co/metatags. Will be used
for Custom Social Media Cards if proxy is true.
The description of the short link generated via api.dub.co/metatags. Will be
used for Custom Social Media Cards if proxy is true.
The image of the short link generated via api.dub.co/metatags. Will be used
for Custom Social Media Cards if proxy is true.
Whether the short link uses link cloaking.
The iOS destination URL for the short link for iOS device targeting.
The Android destination URL for the short link for Android device targeting.
Whether the short link's stats are publicly accessible.
The unique ID of the tag assigned to the short link. This field is deprecated
– use `tags` instead.
The tags assigned to the short link.
The unique ID of the tag.
The name of the tag.
The color of the tag.
The comments for the short link.
The full URL of the short link, including the https protocol (e.g.
`https://dub.sh/try`).
The full URL of the QR code for the short link (e.g.
`https://api.dub.co/qr?url=https://dub.sh/try`).
The UTM source of the short link.
The UTM medium of the short link.
The UTM campaign of the short link.
The UTM term of the short link.
The UTM content of the short link.
The user ID of the creator of the short link.
The workspace ID of the short link.
The number of clicks on the short link.
The date and time when the short link was last clicked.
The date and time when the short link was created.
The date and time when the short link was last updated.
The project ID of the short link. This field is deprecated – use `workspaceId`
instead.
```ts
const { shortLink } = await dub.links.upsert({
url: "https://www.google.com",
});
console.log(shortLink); // will always be https://dub.sh/xnjk23d
```
```ts
{
id: 'clv3o9p9q000au1h0mc7r6l63',
domain: 'dub.sh',
key: 'e7qzMuI',
externalId: "123",
url: 'https://www.google.com',
archived: false,
expiresAt: null,
expiredUrl: null,
password: null,
proxy: false,
title: null,
description: null,
image: null,
rewrite: false,
ios: null,
android: null,
geo: null,
publicStats: false,
tagId: null,
tags: [],
comments: null,
shortLink: 'https://dub.sh/e7qzMuI',
qrCode: 'https://api.dub.co/qr?url=https://dub.sh/e7qzMuI?qr=1',
utmSource: null,
utmMedium: null,
utmCampaign: null,
utmTerm: null,
utmContent: null,
userId: 'cludszk1h0000wmd2e0ea2b0p',
workspaceId: 'ws_clugls3tn000lwfotbpy30304',
clicks: 0,
lastClicked: null,
createdAt: '2024-04-17T10:31:00.398Z',
updatedAt: '2024-04-17T10:31:00.398Z',
projectId: 'clugls3tn000lwfotbpy30304'
}
```
# dub.metatags.get
Retrieve the metatags for a URL.
## Arguments
The URL to retrieve metatags for.
## Response
The meta title tag for the URL.
The meta description tag for the URL.
The OpenGraph image for the URL.
```ts
await dub.metatags.get({
url: "https://dub.co",
});
```
```ts
{
title: 'Dub.co - Link Management for Modern Marketing Teams',
description: 'Dub.co is the open-source link management infrastructure ...',
image: 'https://assets.dub.co/thumbnail.jpg'
}
```
# Overview
TypeScript SDK for Dub.co
## Install
```bash
npm install dub
```
```bash
yarn add dub zod # zod is a peer dependency
```
```bash
pnpm add dub
```
## Options
The SDK constructor accepts an options object with the following properties:
The API token for the Dub.co API. You can create your token from the [Dub.co
dashboard](https://app.dub.co/settings/tokens).
## Usage
```typescript
import { Dub } from "dub";
const dub = new Dub({
token: "DUB_API_KEY",
});
await dub.links.create({
url: "https://google.com",
});
```
# dub.tags.create
Create a new tag for the authenticated workspace.
## Arguments
The name of the tag to create.
The color of the tag. If not provided, a random color will be used from the
list: `red`, `yellow`, `green`, `blue`, `purple`, `pink`, `brown`.
## Response
The unique ID of the tag.
The name of the tag.
The color of the tag.
```ts
await dub.tags.create({
tag: "news",
});
```
```ts
{
id: "clv3j1h7o00032u1ngo8luv84",
name: "news",
color: "blue"
}
```
# dub.tags.list
Retrieve a list of tags for the authenticated workspace.
## Response
The unique ID of the tag.
The name of the tag.
The color of the tag.
```ts
await dub.tags.list();
```
```ts
[
{
id: "clv3j1h7o00032u1ngo8luv84",
name: "news",
color: "blue",
},
{
id: "cluibmjj5000975qol4hnx0t7",
name: "work",
color: "red",
},
];
```
# dub.tags.update
Update a tag for the authenticated user.
## Arguments
The name of the tag to update.
The color of the tag.
## Response
The unique ID of the tag.
The name of the tag.
The color of the tag.
```ts
await dub.tags.update("cluibmjj5000975qol4hnx0t7", {
name: "Jobs",
color: "red",
});
```
```ts
{
id: "clv3j1h7o00032u1ngo8luv84",
name: "news",
color: "blue"
}
```
# dub.workspaces.get
Retrieve a workspace for the authenticated user.
## Arguments
The unique ID or slug of the workspace.
## Response
The unique ID of the workspace.
The name of the workspace.
The slug of the workspace.
The logo of the workspace.
The usage of the workspace.
The usage limit of the workspace.
The links usage of the workspace.
The links limit of the workspace.
The domains limit of the workspace.
The tags limit of the workspace.
The users limit of the workspace.
The plan of the workspace.
The Stripe ID of the workspace.
The date and time when the billing cycle starts for the workspace.
The date and time when the workspace was created.
The role of the authenticated user in the workspace.
The role of the authenticated user in the workspace.
The domains of the workspace.
The domain of the workspace.
Indicates if the domain is the primary domain.
```ts
// Retrieve a workspace by ID
await dub.workspaces.get({
idOrSlug: "clv3mw4qb00008tvuw86c53db,
});
// Retrieve a workspace by slug
await dub.workspaces.get({
idOrSlug: "acme",
});
```
```ts
{
id: 'clv3mw4qb00008tvuw86c53db',
name: 'Acme',
slug: 'acme',
logo: null,
usage: 0,
usageLimit: 1000,
linksUsage: 0,
linksLimit: 25,
domainsLimit: 3,
tagsLimit: 5,
usersLimit: 1,
plan: 'free',
stripeId: null,
billingCycleStart: 17,
createdAt: '2024-04-17T09:52:27.635Z',
users: [ { role: 'owner' } ],
domains: []
}
```
# dub.workspaces.update
Update a workspace for the authenticated user.
## Arguments
The unique ID or slug of the workspace.
The name of the workspace.
The slug of the workspace.
## Response
The unique ID of the workspace.
The name of the workspace.
The slug of the workspace.
The logo of the workspace.
The usage of the workspace.
The usage limit of the workspace.
The links usage of the workspace.
The links limit of the workspace.
The domains limit of the workspace.
The tags limit of the workspace.
The users limit of the workspace.
The plan of the workspace.
The Stripe ID of the workspace.
The date and time when the billing cycle starts for the workspace.
The date and time when the workspace was created.
The role of the authenticated user in the workspace.
The role of the authenticated user in the workspace.
The domains of the workspace.
The domain of the workspace.
Indicates if the domain is the primary domain.
```ts
// Update a workspace by its slug
await dub.workspaces.update("acme", {
name: "Acme Inc",
});
// Update a workspace by its ID
await dub.workspaces.update("ws_clv3mw4qb00008tvuw86c53db", {
name: "Acme Inc",
});
```
```ts
{
id: 'clv3mw4qb00008tvuw86c53db',
name: 'Acme',
slug: 'acme',
logo: null,
usage: 0,
usageLimit: 1000,
linksUsage: 0,
linksLimit: 25,
domainsLimit: 3,
tagsLimit: 5,
usersLimit: 1,
plan: 'free',
stripeId: null,
billingCycleStart: 17,
createdAt: '2024-04-17T09:52:27.635Z',
users: [ { role: 'owner' } ],
domains: []
}
```
# Self-hosting Dub.co
An end-to-end guide on how to self-host Dub.co – the open-source link management platform.
You can self-host Dub.co 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.co 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.co 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.co 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.co 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.
![Upstash Redis database](https://mintlify.s3-us-west-1.amazonaws.com/dub/images/upstash-create-db.png)
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.
![Upstash Redis tokens](https://mintlify.s3-us-west-1.amazonaws.com/dub/images/upstash-redis-tokens.png)
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.
![Upstash QStash tokens](https://mintlify.s3-us-west-1.amazonaws.com/dub/images/upstash-qstash-tokens.png)
## 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**.
![PlanetScale choose framework](https://mintlify.s3-us-west-1.amazonaws.com/dub/images/planetscale-choose-framework.png)
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.
![PlanetScale add credentials](https://mintlify.s3-us-west-1.amazonaws.com/dub/images/planetscale-add-credentials.png)
In your Dub.co 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
npx prisma generate
```
Then, create the database tables with the following command:
```bash Terminal
npx prisma db 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.co 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-social-media-cards) images
We recommend using [Cloudflare R2](https://cloudflare.com/r2) for self-hosting Dub.co, 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.
![Cloudflare R2 bucket](https://mintlify.s3-us-west-1.amazonaws.com/dub/images/cloudflare-r2-create-bucket.png)
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.
![Cloudflare manage API tokens](https://mintlify.s3-us-west-1.amazonaws.com/dub/images/cloudflare-r2-manage-api-tokens.png)
Then, click **Create API Token**.
![Cloudflare R2 API token](https://mintlify.s3-us-west-1.amazonaws.com/dub/images/cloudflare-r2-create-api-token.png)
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
```
![Cloudflare R2 domain](https://mintlify.s3-us-west-1.amazonaws.com/dub/images/cloudflare-r2-public-domain.png)
## 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-social-media-cards) feature. You'll need to set up an Unsplash application to get an access key.
![Custom social media
cards](https://assets.dub.co/changelog/custom-social-cards.png)
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`.
![Vercel Framework Preset and Root Directory](https://mintlify.s3-us-west-1.amazonaws.com/dub/images/vercel-framework-preset.png)
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:
![Vercel Deploy settings](https://mintlify.s3-us-west-1.amazonaws.com/dub/images/vercel-deploy-settings.png)
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:
![Whitelabeled Login](https://mintlify.s3-us-west-1.amazonaws.com/dub/images/whitelabeled-login.png)
## Caveats
This guide is meant to be a starting point for self-hosting Dub.co. 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.co 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.