Skip to contentSkip to navigationSkip to topbar
On this page
Looking for more inspiration?Visit the
(information)
You're in the right place! Segment documentation is now part of Twilio Docs. The content you are used to is still here—just in a new home with a refreshed look.

Build a Destination


This document describes in detail the steps to create a new Actions-based destination using the Segment CLI.


Prerequisites

prerequisites page anchor

Before you begin, consider the following prerequisites.

Security

security page anchor

The security of customers and partners is a top priority at Segment. Before you begin building, review the Acceptable Use Policy(link takes you to an external page), and keep in mind:

  • Follow a secure software-development lifecycle, which lets you both create code that is safe for Segment customers and their end users, and maintain and raise the security of that code over time.
  • If you or your code comes into contact with Segment customer or end-user data for any reason, protect it with commercially reasonable methods throughout the data lifecycle, including creating, handling, transporting, and destruction.
  • If you suspect a security event, incident, or breach related to this project, contact Segment Security for assistance with your investigation and communications.
  • Practice modern and common-sense security for any scenario that is not explicitly stated.

Configure your development environment

configure-your-development-environment page anchor

You don't need to access a Segment dev environment to build an integration. You can test it locally on your machine. Destinations are written in TypeScript. For more information about TypeScript, see TypeScript's documentation(link takes you to an external page).

To work with Segment's actions repository, download and install the following:

If you encounter errors when setting up your development environment, ensure you're running the correct version of Node by running nvm use.

Fork the segmentio/action-destinations repository, connect to NPM and Yarn, and ensure a compatible version of Node is installed.

(information)

Enable GitHub Actions

Action-based destinations run several workflows on pull requests, which requires that GitHub actions be enabled in the repository. To prevent workflow failure, you must enable GitHub Actions on the Actions tab of the forked repository.

Run the test suite to ensure the environment is properly configured.

1
# Clone the repo locally
2
git clone <your fork or https://github.com/segmentio/action-destinations.git>
3
cd action-destinations
4
5
npm login
6
yarn login
7
8
# Requires node 18.17.1, optionally: nvm use 18.17.1
9
yarn --ignore-optional
10
yarn install
11
yarn build
12
13
# Run unit tests to ensure things are working! For partners who don't have access to internal packages, you can run:
14
yarn test-partners
15
16
# For segment employees, you can run:
17
yarn test
18
19
# to reset all caches and rebuild again
20
yarn clean-build

Once you've configured your environment, you're ready to begin building your first destination. All commands, unless noted otherwise, should run from the root of the project folder. For example, ./action-destinations

Run ./bin/run --help at any time or visit the CLI README(link takes you to an external page) to see a list of available commands.

Scaffold the new destination

scaffold-the-new-destination page anchor

To begin, run ./bin/run init to scaffold the project's directory structure, and create a minimal implementation of the new destination. The initialization sets the following information:

  • Integration name
  • Integration slug
  • Authentication template (choose one of Custom Auth, Browser Destination (experimental), Basic Auth, OAuth2 Auth, or Minimal)

After completion, the directory structure of the new destination is created at packages/destination-actions/src/destinations/<slug>. The init command does not register or deploy the integration.

The index.ts file in this folder contains the beginnings of an Actions-based Destination. For example, a destination named Test using Basic Auth contains the following:

1
import type { DestinationDefinition } from '@segment/actions-core'
2
import type { Settings } from './generated-types'
3
4
const destination: DestinationDefinition<Settings> = {
5
name: 'Test',
6
slug: 'actions-test',
7
mode: 'cloud',
8
9
authentication: {
10
scheme: 'basic',
11
fields: {
12
username: {
13
label: 'Username',
14
description: 'Your Test username',
15
type: 'string',
16
required: true
17
},
18
password: {
19
label: 'password',
20
description: 'Your Test password.',
21
type: 'string',
22
required: true
23
}
24
},
25
testAuthentication: (request) => {
26
// Return a request that tests/validates the user's credentials.
27
// If you do not have a way to validate the authentication fields safely,
28
// you can remove the `testAuthentication` function, though discouraged.
29
}
30
},
31
32
extendRequest({ settings }) {
33
return {
34
username: settings.username,
35
password: settings.password
36
}
37
},
38
39
onDelete: async (request, { settings, payload }) => {
40
// Return a request that performs a GDPR delete for the provided Segment userId or anonymousId
41
// provided in the payload. If your destination does not support GDPR deletion you should not
42
// implement this function and should remove it completely.
43
},
44
45
actions: {}
46
}
47
48
export default destination

Notice the name and slug properties, the authentication object, an extendRequest function that returns the username and password from settings, and an empty actions object.

With this minimal configuration, the destination can connect to the Segment App's user interface, and collect authentication fields. The destination does not do anything at this point, because no Actions are defined.

The testAuthentication function verifies the user's credentials against a service. For testing, enter return true in this function to continue development.

The onDelete function performs a GDPR delete against a service. For testing, enter return true in this function to continue development.

Browser (device-mode) destination

browser-device-mode-destination page anchor
1
import type { Settings } from './generated-types'
2
import type { BrowserDestinationDefinition } from '../../lib/browser-destinations'
3
import { browserDestination } from '../../runtime/shim'
4
5
// Declare global to access your client
6
declare global {
7
interface Window {
8
sdkName: typeof sdkName
9
}
10
}
11
12
// Switch from unknown to the partner SDK client types
13
export const destination: BrowserDestinationDefinition<Settings, unknown> = {
14
name: 'BrowserExample',
15
slug: 'actions-browserexample',
16
mode: 'device',
17
18
settings: {
19
// Add any Segment destination settings required here
20
},
21
22
initialize: async ({ settings, analytics }, deps) => {
23
await deps.loadScript('<path_to_partner_script>')
24
// initialize client code here
25
26
return window.yourSDKName
27
},
28
29
actions: {}
30
}
31
32
export default browserDestination(destination)

In Browser Destinations, no authentication is required. Instead, you must initialize your SDK with the required settings needed.

When importing your SDK, Segment recommends loading from a CDN when possible. This keeps the bundle size lower rather than directly including the SDK in Segment's package.

Make sure to add a global declaration where you specify your SDK as a field of a Window interface so you can reference and return it in your initialize function.


Actions define what the destination can do. They instruct Segment how to send data to your destination API. For example, consider this "Post to Channel" action from a Slack destination:

1
const destination = {
2
// ...other properties
3
actions: {
4
postToChannel: {
5
// the human-friendly display name of the action
6
title: 'Post to Channel',
7
8
// the human-friendly description of the action. supports markdown
9
description: 'Post a message to a Slack channel',
10
11
// fql query to use for the subscription initially
12
defaultSubscription: 'type = "track"'
13
14
// the set of fields that are specific to this action
15
fields: {
16
webhookUrl: {
17
label: 'Webhook URL',
18
description: 'Slack webhook URL.',
19
type: 'string',
20
format: 'uri',
21
required: true
22
},
23
text: {
24
label: 'Message',
25
description: "The text message to post to Slack. You can use [Slack's formatting syntax.](https://api.slack.com/reference/surfaces/formatting)",
26
type: 'string',
27
required: true
28
}
29
},
30
31
// the final logic and request to send data to the destination's API
32
perform: (request, { settings, payload }) => {
33
return request.post(payload.webhookUrl, {
34
responseType: 'text',
35
json: {
36
text: payload.text
37
}
38
})
39
}
40
}
41
}
42
}

Actions should map to a feature in your platform. Try to keep the action atomic. The action should perform a single operation in the downstream platform.

Define and scaffold an action

define-and-scaffold-an-action page anchor

As mentioned above, actions contain the behavior and logic necessary for sending data to your platform's API.

To create the Post to Channel action above, begin by creating the scaffold on top of which you'll build the action. Run ./bin/run generate:action postToChannel server to create the scaffold.

The generate:action command takes two arguments:

  • The name of the action
  • The type of action

When you create a scaffold, the CLI also imports the action to the definition of the destination, and generates empty types based on the action's fields.

Add functionality to the action

add-functionality-to-the-action page anchor

After you've created the scaffold for the action, add logic that defines what the action does. Here, you'll define the fields that the action expects to receive, and write the code that performs the action.

Action fields

action-fields page anchor

For each action or authentication scheme, you define a collection of inputs and fields. Input fields define what the user sees in the Action Editor within the Segment App. In an action, these fields accept input from the incoming Segment event.

The Segment CLI introspects field definitions when you run ./bin/run generate:types to generate their TypeScript declarations. This ensures the perform function is strongly-typed.

Define fields following the field schema. If your editor or IDE provides good Intellisense and autocompletion, you should see the allowed properties.

As mentioned above, the perform function contains the code that defines what the action does.

Segment recommends that you start with a simple task, and evolve it. Get the basics working first. Add one or two fields to start, then run ./bin/run generate:types when you change the definition of a field. Run this step manually after changes, or run yarn types --watch to regenerate types when a change is detected.


Testing ensures that your destination functions the way you expect. For information on testing, see Test your destination.