From 49cdad04bc15ffd677f5475bdf63c969c0bdb02e Mon Sep 17 00:00:00 2001 From: stayseesong Date: Fri, 16 Dec 2022 15:46:07 -0800 Subject: [PATCH 01/74] Analytics Node Next --- .../catalog/libraries/server/node/next.md | 616 ++++++++++++++++++ 1 file changed, 616 insertions(+) create mode 100644 src/connections/sources/catalog/libraries/server/node/next.md diff --git a/src/connections/sources/catalog/libraries/server/node/next.md b/src/connections/sources/catalog/libraries/server/node/next.md new file mode 100644 index 0000000000..2be6e66c77 --- /dev/null +++ b/src/connections/sources/catalog/libraries/server/node/next.md @@ -0,0 +1,616 @@ +--- +title: Analytics for Node.js Next +repo: analytics-node +strat: node-js +--- + +Segment's Analytics Node.js Next library lets you record analytics data from your node code. The requests hit Segment's servers, and then Segment routes your data to any destinations you have enabled. + +The [Segment Analytics Node.js Next library is open-source](https://github.com/segmentio/analytics-next){:target="_blank"} on GitHub. + +All of Segment's server-side libraries are built for high-performance, so you can use them in your web server controller code. This library uses an internal queue to make `identify` and `track` calls non-blocking and fast. It also batches messages and flushes asynchronously to Segment's servers. + +## Getting Started + +> warning "" +> Make sure you're using a version of Node that's 14 or higher. + +Run: + +```bash +# npm +npm install @segment/analytics-node +# yarn +yarn add @segment/analytics-node +# pnpm +pnpm install @segment/analytics-node +``` + +This will add Segment's Node library module to your `package.json`. The module exposes an `Analytics` constructor, which you need to initialize with your Segment source's **Write Key**, like so: + +```javascript +var Analytics = require('analytics-node'); +var analytics = new Analytics('YOUR_WRITE_KEY'); +``` + +Be sure to replace `YOUR_WRITE_KEY` with your actual **Write Key** which you can find in Segment under your source settings. + +This will create an instance of `Analytics` that you can use to send data to Segment for your project. The default initialization settings are production-ready and queue 20 messages before sending any requests. In development you might want to use [development settings](/docs/connections/sources/catalog/libraries/server/node/#development). + +### Regional configuration +For Business plans with access to [Regional Segment](/docs/guides/regional-segment), you can use the `host` configuration parameter to send data to the desired region: +1. Oregon (Default) — `api.segment.io/v1` +2. Dublin — `events.eu1.segmentapis.com` + +An example of setting the host to the EU endpoint using the Node library is: +```javascript +const analytics = new Analytics({ + ... + host: "https://events.eu1.segmentapis.com" +}); +``` + +## Basic tracking methods +The basic tracking methods below serve as the building blocks of your Segment tracking. They include [Identify](#identify), [Track](#track), [Page](#page), [Group](#group), and [Alias](#alias). + +These methods correspond with those used in the [Segment Spec](/docs/connections/spec/). The documentation on this page explains how to use these methods in Analytics Node.js Next. + + +### Identify + +> info "Good to know" +> For any of the different methods described on this page, you can replace the properties and traits in the code samples with variables that represent the data collected. + +`identify` lets you tie a user to their actions and record traits about them. It includes a unique User ID and/or anonymous ID, and any optional traits you know about them. + +You should call `identify` once when the user's account is first created, and then again any time their traits change. + +Example of an anonymous `identify` call: + +```javascript +analytics.identify({ + anonymousId: '48d213bb-95c3-4f8d-af97-86b2b404dcfe', + traits: { + friends: 42 + } +}); +``` + +This call identifies the user and records their unique anonymous ID, and labels them with the `friends` trait. + +Example of an `identify` call for an identified user: + +```javascript +analytics.identify({ + userId: '019mr8mf4r', + traits: { + name: 'Michael Bolton', + email: 'mbolton@example.com', + plan: 'Enterprise', + friends: 42 + } +}); +``` +The call above identifies Michael by his unique User ID (the one you know him by in your database), and labels him with the `name`, `email`, `plan` and `friends` traits. + +The `identify` call has the following fields: + + + + + + + + + + + + + + + + + + + + + + +
`userId` _String, optional_The ID for this user in your database. _Note: at least one of `userId` or `anonymousId` must be included in any identify call._
`anonymousId` _String, optional_An ID associated with the user when you don't know who they are (for example, [the anonymousId generated by `analytics.js`](/docs/connections/sources/catalog/libraries/website/javascript/#anonymous-id)). _Note: You must include at least one of `userId` or `anonymousId` in all identify calls._
`traits` _Object, optional_A dictionary of [traits](/docs/connections/spec/identify#traits) you know about the user. Things like: `email`, `name` or `friends`.
`timestamp` _Date, optional_A JavaScript date object representing when the identify took place. If the identify just happened, leave it out and we'll use the server's time. If you're importing data from the past make sure you to send a `timestamp`.
`context` _Object, optional_A dictionary of extra [context](https://segment.com/docs/connections/spec/common/#context) to attach to the call. _Note: `context` differs from `traits` because it is not attributes of the user itself._
+ +Find details on the **identify method payload** in our [Spec](/docs/connections/spec/identify/). + +### Track + +`track` lets you record the actions your users perform. Every action triggers what we call an "event", which can also have associated properties. + +You'll want to track events that are indicators of success for your site, like **Signed Up**, **Item Purchased** or **Article Bookmarked**. + +To get started, we recommend tracking just a few important events. You can always add more later! + +Example anonymous `track` call: + +```javascript +analytics.track({ + anonymousId: '48d213bb-95c3-4f8d-af97-86b2b404dcfe', + event: 'Item Purchased', + properties: { + revenue: 39.95, + shippingMethod: '2-day' + } +}); +``` + +Example identified `track` call: + +```javascript +analytics.track({ + userId: '019mr8mf4r', + event: 'Item Purchased', + properties: { + revenue: 39.95, + shippingMethod: '2-day' + } +}); +``` + +This example `track` call tells us that your user just triggered the **Item Purchased** event with a revenue of $39.95 and chose your hypothetical '2-day' shipping. + +`track` event properties can be anything you want to record. In this case, revenue and shipping method. + +The `track` call has the following fields: + + + + + + + + + + + + + + + + + + + + + + + + + + +
`userId` _String, optional_The ID for this user in your database. _Note: at least one of `userId` or `anonymousId` must be included in any track call._
`anonymousId` _String, optional_An ID associated with the user when you don't know who they are (for example, [the anonymousId generated by `analytics.js`](/docs/connections/sources/catalog/libraries/website/javascript/#anonymous-id)). _Note: You must include at least one of `userId` or `anonymousId` in all track calls._
`event` _String_The name of the event you're tracking. We recommend human-readable names like `Song Played` or `Status Updated`.
`properties` _Object, optional_A dictionary of properties for the event. If the event was `Product Added`, it might have properties like `price` or `product`.
`timestamp` _Date, optional_A JavaScript date object representing when the track took place. If the track just happened, leave it out and we'll use the server's time. If you're importing data from the past make sure you to send a `timestamp`.
`context` _Object, optional_A dictionary of extra [context](https://segment.com/docs/connections/spec/common/#context) to attach to the call. _Note: `context` differs from `traits` because it is not attributes of the user itself._
+ +Find details on **best practices in event naming** as well as the **`track` method payload** in our [Spec](/docs/connections/spec/track/). + +### Page + +The [`page`](/docs/connections/spec/page/) method lets you record page views on your website, along with optional extra information about the page being viewed. + +If you're using our client-side set up in combination with the Node.js library, page calls are **already tracked for you** by default. However, if you want to record your own page views manually and aren't using our client-side library, read on! + +Example `page` call: + +```js +analytics.page({ + userId: '019mr8mf4r', + category: 'Docs', + name: 'Node.js Library', + properties: { + url: 'https://segment.com/docs/connections/sources/catalog/librariesnode', + path: '/docs/connections/sources/catalog/librariesnode/', + title: 'Node.js Library - Segment', + referrer: 'https://github.com/segmentio/analytics-node' + } +}); +``` + +The `page` call has the following fields: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
`userId` _String, optional_The ID for this user in your database. _Note: at least one of `userId` or `anonymousId` must be included in any page call._
`anonymousId` _String, optional_An ID associated with the user when you don't know who they are (eg., [the anonymousId generated by `analytics.js`](/docs/connections/sources/catalog/libraries/website/javascript/#anonymous-id)). _Note: at least one of `userId` or `anonymousId` must be included in any page call._
`category` _String, optional_The category of the page. Useful for things like ecommerce where many pages often live under a larger category.
`name` _String, optional_The name of the page, for example **Signup** or **Home**.
`properties` _Object, optional_A dictionary of properties of the page. A few properties specially recognized and automatically translated: `url`, `title`, `referrer` and `path`, but you can add your own too!
`timestamp` _Date, optional_A JavaScript date object representing when the track took place. If the track just happened, leave it out and we'll use the server's time. If you're importing data from the past make sure you to send a `timestamp`.
`context` _Object, optional_A dictionary of extra [context](https://segment.com/docs/connections/spec/common/#context) to attach to the call. _Note: `context` differs from `traits` because it is not attributes of the user itself._
+ +Find details on the **`page` payload** in our [Spec](/docs/connections/spec/page/). + +### Group + +`group` lets you associate an [identified user](/docs/connections/sources/catalog/libraries/server/node/#identify) with a group. A group could be a company, organization, account, project or team! It also lets you record custom traits about the group, like industry or number of employees. + +This is useful for tools like [Intercom](/docs/connections/destinations/catalog/intercom/), [Preact](/docs/connections/destinations/catalog/preact/) and [Totango](/docs/connections/destinations/catalog/totango/), as it ties the user to a **group** of other users. + +Example `group` call: + +```javascript +analytics.group({ + userId: '019mr8mf4r', + groupId: '56', + traits: { + name: 'Initech', + description: 'Accounting Software' + } +}); +``` + +The `group` call has the following fields: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
`userId` _String, optional_The ID for this user in your database. _Note: at least one of `userId` or `anonymousId` must be included in any group call._
`anonymousId` _String, optional_An ID associated with the user when you don't know who they are (eg., [the anonymousId generated by `analytics.js`](/docs/connections/sources/catalog/libraries/website/javascript/#anonymous-id)). _Note: at least one of `userId` or `anonymousId` must be included in any group call._
`groupId` _stringThe ID of the group.
`traits` _dict, optional_A dict of traits you know about the group. For a company, they might be things like `name`, `address`, or `phone`.
`context` _dict, optional_A dict containing any context about the request. To see the full reference of supported keys, check them out in the [context reference](/docs/connections/spec/common/#context)
`timestamp` _datetime, optional_A `datetime` object representing when the group took place. If the group just happened, leave it out and we'll use the server's time. If you're importing data from the past make sure you send `timestamp`.
`integrations` _dict, optional_A dictionary of destinations to enable or disable
+ +Find more details about `group`, including the **`group` payload**, in our [Spec](/docs/connections/spec/group/). + +### Alias + +The `alias` call allows you to associate one identity with another. This is an advanced method and should not be widely used, but is required to manage user identities in _some_ destinations. Other destinations do not support the alias call. + +In [Mixpanel](/docs/connections/destinations/catalog/mixpanel/#alias) it's used to associate an anonymous user with an identified user once they sign up. For [Kissmetrics](/docs/connections/destinations/catalog/kissmetrics/#alias), if your user switches IDs, you can use 'alias' to rename the 'userId'. + +Example `alias` call: + +```javascript +analytics.alias({ + previousId: 'old_id', + userId: 'new_id' +}); +``` + +The `alias` call has the following fields: + + + + + + + + + + +
`userId` _String_The ID for this user in your database.
`previousId` _String_The previous ID to alias from.
+ +Here's a full example of how Segment might use the `alias` call: + +```javascript +// the anonymous user does actions ... +analytics.track({ userId: 'anonymous_user', event: 'Anonymous Event' }) +// the anonymous user signs up and is aliased +analytics.alias({ previousId: 'anonymous_user', userId: 'identified@example.com' }) +// the identified user is identified +analytics.identify({ userId: 'identified@example.com', traits: { plan: 'Free' } }) +// the identified user does actions ... +analytics.track({ userId: 'identified@example.com', event: 'Identified Action' }) +``` + +For more details about `alias`, including the **`alias` call payload**, check out the Segment [Spec](/docs/connections/spec/alias/). + +--- + + +## Configuration + +The second argument to the `Analytics` constructor is an optional list of settings to configure the module. + +```javascript +const analytics = new Analytics({ + writeKey: '', + host: 'https://api.segment.io', + path: '/v1/batch', + maxRetries: 3, + maxEventsInBatch: 15, + flushInterval: 10000, + // ... and more! + }) +``` + +Setting | Details +------- | -------- +`writeKey` _string_ | The key that corresponds to your Segment.io project +`host` _string_ | The base URL of the API. The default is: "https://api.segment.io" +`path` _string_ | The API path route. The default is: "/v1/batch" +`maxRetries` _number_ | The number of times to retry flushing a batch. The default is: `3` +`maxEventsInBatch` _number_ | The number of messages to enqueue before flushing. The default is: `15` +`flushInterval` _number_ | The number of milliseconds to wait before flushing the queue automatically. The default is: `10000` +`httpRequestTimeout` | The maximum number of milliseconds to wait for an http request. The default is: `10000` + +### Graceful shutdown +Avoid losing events after shutting down your console. Call `.closeAndFlush()` to stop collecting new events and flush all existing events. If a callback on an event call is included, this also waits for all callbacks to be called, and any of their subsequent promises to be resolved. + +```javascript +await analytics.closeAndFlush() +// or +await analytics.closeAndFlush({ timeout: 5000 }) // force resolve after 5000ms +``` + +Here's an example of how to use graceful shutdown: +```javascript +const app = express() +const server = app.listen(3000) + +const onExit = async () => { + await analytics.closeAndFlush() + server.close(() => { + console.log("Gracefully closing server...") + process.exit() + }) +} +['SIGINT', 'SIGTERM'].forEach((code) => process.on(code, onExit)) +``` + +### Collect unflushed events +If you need to preserve all of your events in the instance of a forced timeout, even ones that came in after analytics.closeAndFlush() was called, you can still collect those events by using: + +```javascript +const unflushedEvents = [] + +analytics.on('call_after_close', (event) => unflushedEvents.push(events)) +await analytics.closeAndFlush() + +console.log(unflushedEvents) // all events that came in after closeAndFlush was called +``` + +## Error handling + +To keep track of errors, subscribe and log all event delivery errors by running: + +```javascript +const analytics = new Analytics({ writeKey: '' }) + +analytics.on('error', (err) => console.error(err)) +``` + + +### Event emitter interface +The event emitter interface allows you to track when certain things happen in the app, such as a track call or an error, and it will call the function you provided with some arguments when that event happens. + +```javascript +analytics.on('error', (err) => console.error(err)) + +analytics.on('identify', (ctx) => console.log(err)) + +analytics.on('track', (ctx) => console.log(ctx)) +``` + + +## Development + +You can use this initialization during development to make the library flush every time a message is submitted, so that you can be sure your calls are working properly before pushing to production. + +```javascript +var analytics = new Analytics('YOUR_WRITE_KEY', { flushAt: 1 }); +``` + + +## Selecting Destinations + +The `alias`, `group`, `identify`, `page` and `track` calls can all be passed an object of `integrations` that lets you turn certain destinations on or off. By default all destinations are enabled. + +Here's an example with the `integrations` object shown: + +```javascript +analytics.track({ + event: 'Membership Upgraded', + userId: '97234974', + integrations: { + 'All': false, + 'Vero': true, + 'Google Analytics': false + } +}) +``` + +In this case, Segment specifies that they want this `track` to only go to Vero. `All: false` says that no destination should be enabled unless otherwise specified. `Vero: true` turns on Vero. + +Destination flags are **case sensitive** and match [the destination's name in the docs](/docs/connections/destinations/) (for example, "AdLearn Open Platform", "awe.sm", "MailChimp"). In some cases, there may be several names for a destination; if that happens you'll see a "Adding (destination name) to the Integrations Object" section in the destination's doc page with a list of valid names. + +**Note:** + +- Available at the business level, filtering track calls can be done right from the Segment UI on your source schema page. We recommend using the UI if possible since it's a much simpler way of managing your filters and can be updated with no code changes on your side. + +- If you are on a grandfathered plan, events sent server-side that are filtered through the Segment dashboard will still count towards your API usage. + +## Historical Import + +You can import historical data by adding the `timestamp` argument to any of your method calls. This can be helpful if you've just switched to Segment. + +Historical imports can only be done into destinations that can accept historical timestamped data. Most analytics tools like Mixpanel, Amplitude, Kissmetrics, etc. can handle that type of data just fine. One common destination that does not accept historical data is Google Analytics since their API cannot accept historical data. + +**Note:** If you're tracking things that are happening right now, leave out the `timestamp` and Segment's servers will timestamp the requests for you. + + +## Batching + +Segment's libraries are built to support high performance environments. That means it is safe to use Segment's Node library on a web server that's serving hundreds of requests per second. + +Every method you call **doesn't** result in a HTTP request, but is queued in memory instead. Messages are then flushed in batch in the background, which allows for much faster operation. + +By default, Segment's library will flush: + + - The very first time it gets a message. + - Every 15 messages (controlled by `settings.maxEventsInBatch`). + - If 10 seconds has passed since the last flush (controlled by `settings.flushInterval`) + +There is a maximum of `500KB` per batch request and `32KB` per call. + +If you don't want to batch messages, you can turn batching off by setting the `maxEventsInBatch` setting to `1`, like so: + +```javascript +const analytics = new Analytics({ + ... + maxEventsInBatch: 1 +});``` + +Batching means that your message might not get sent right away. Every method call takes an optional `callback`, which you can use to know when a particular message is flushed from the queue, like so: + +```javascript +analytics.track({ + userId: '019mr8mf4r', + event: 'Ultimate Played', + }, + (err, ctx) => { + ... + } +) +``` + +You can also flush on demand. For example, at the end of your program, you need to flush to make sure that nothing is left in the queue. To do that, call the `flush` method: + +```javascript +analytics.flush(function(err, batch){ + console.log('Flushed, and now this program can exit!'); +}); +``` + + + + + + + +## Multiple Clients + +Different parts of your application may require different types of batching, or even sending to multiple Segment sources. In that case, you can initialize multiple instances of `Analytics` with different settings: + +```javascript +const marketingAnalytics = new Analytics({ writeKey: 'MARKETING_WRITE_KEY' }); +const appAnalytics = new Analytics({ writeKey: 'APP_WRITE_KEY' }); +``` + + +## Troubleshooting + +{% include content/troubleshooting-intro.md %} +{% include content/troubleshooting-server-debugger.md %} +{% include content/troubleshooting-server-integration.md %} From 66a2ca8b0ff76f5f16a8a68b1927a14d4829e6f8 Mon Sep 17 00:00:00 2001 From: stayseesong Date: Fri, 16 Dec 2022 16:27:15 -0800 Subject: [PATCH 02/74] edits --- .../catalog/libraries/server/node/index.md | 7 +- .../catalog/libraries/server/node/next.md | 134 +++++++++++++++++- .../libraries/server/node/quickstart.md | 5 +- 3 files changed, 141 insertions(+), 5 deletions(-) diff --git a/src/connections/sources/catalog/libraries/server/node/index.md b/src/connections/sources/catalog/libraries/server/node/index.md index e3d9f08e1c..46ea81e56d 100644 --- a/src/connections/sources/catalog/libraries/server/node/index.md +++ b/src/connections/sources/catalog/libraries/server/node/index.md @@ -1,11 +1,14 @@ --- -title: Analytics for Node.js +title: Analytics for Node.js Classic redirect_from: '/connections/sources/catalog/libraries/server/node-js/' repo: analytics-node strat: node-js --- -Our Node.js library lets you record analytics data from your node code. The requests hit our servers, and then we route your data to any destinations you have enabled. +> warning "Deprecation of Analytics Node.js Classic" +> On [date], Segment will end support for Analytics Node.js Classic, which includes versions [1.x.x] and older. Upgrade to Analytics Node.js 2.0. See the [Analytics Node.js 2.0] docs to learn more. + +Segment's Node.js library lets you record analytics data from your node code. The requests hit our servers, and then Segment routes your data to any destinations you have enabled. The [Segment Node.js library is open-source](https://github.com/segmentio/analytics-node) on GitHub. diff --git a/src/connections/sources/catalog/libraries/server/node/next.md b/src/connections/sources/catalog/libraries/server/node/next.md index 2be6e66c77..9e93819bba 100644 --- a/src/connections/sources/catalog/libraries/server/node/next.md +++ b/src/connections/sources/catalog/libraries/server/node/next.md @@ -1,15 +1,19 @@ --- -title: Analytics for Node.js Next +title: Analytics for Node.js 2.0 repo: analytics-node strat: node-js --- -Segment's Analytics Node.js Next library lets you record analytics data from your node code. The requests hit Segment's servers, and then Segment routes your data to any destinations you have enabled. +Segment's Analytics Node.js 2.0 library lets you record analytics data from your node code. The requests hit Segment's servers, and then Segment routes your data to any destinations you have enabled. The [Segment Analytics Node.js Next library is open-source](https://github.com/segmentio/analytics-next){:target="_blank"} on GitHub. All of Segment's server-side libraries are built for high-performance, so you can use them in your web server controller code. This library uses an internal queue to make `identify` and `track` calls non-blocking and fast. It also batches messages and flushes asynchronously to Segment's servers. +> info "Using Analytics for Node.js Classic?" +> If you’re still using the classic version of Analytics for Node.js, you can refer to the documentation [here](/docs/connections/sources/catalog/libraries/server/node/). +>

On [date], Segment will end support for Analytics Node.js Classic, which includes versions [#] and older. Upgrade to Analytics Node.js 2.0. See the Analytics Node.js 2.0 docs to learn more. + ## Getting Started > warning "" @@ -430,6 +434,132 @@ analytics.on('identify', (ctx) => console.log(err)) analytics.on('track', (ctx) => console.log(ctx)) ``` +## Plugin architecture +When you develop against Analytics 2.0, the plugins you write can augment functionality, enrich data, and control the flow and delivery of events. From modifying event payloads to changing analytics functionality, plugins help to speed up the process of getting things done. + +Though middlewares function the same as plugins, it's best to use plugins as they are easier to implement and are more testable. + +### Plugin categories +Plugins are bound by Analytics 2.0 which handles operations such as observability, retries, and error handling. There are two different categories of plugins: +* **Critical Plugins**: Analytics.js expects this plugin to be loaded before starting event delivery. Failure to load a critical plugin halts event delivery. Use this category sparingly, and only for plugins that are critical to your tracking. +* **Non-critical Plugins**: Analytics.js can start event delivery before this plugin finishes loading. This means your plugin can fail to load independently from all other plugins. For example, every Analytics.js destination is a non-critical plugin. This makes it possible for Analytics.js to continue working if a partner destination fails to load, or if users have ad blockers turned on that are targeting specific destinations. + +> info "" +> Non-critical plugins are only non-critical from a loading standpoint. For example, if the `before` plugin crashes, this can still halt the event delivery pipeline. + +Non-critical plugins run through a timeline that executes in order of insertion based on the entry type. Segment has these five entry types of non-critical plugins: + +| Type | Details | +| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `before` | Executes before event processing begins. These are plugins that run before any other plugins run.

For example, validating events before passing them along to other plugins. A failure here could halt the event pipeline.

See the example of how Analytics.js uses the [Event Validation plugin](https://github.com/segmentio/analytics-next/blob/master/packages/browser/src/plugins/validation/index.ts){:target="_blank"} to verify that every event has the correct shape. | +| `enrichment` | Executes as the first level of event processing. These plugins modify an event.

See the example of how Analytics.js uses the [Page Enrichment plugin](https://github.com/segmentio/analytics-next/blob/master/packages/browser/src/plugins/page-enrichment/index.ts){:target="_blank"} to enrich every event with page information. | +| `destination` | Executes as events begin to pass off to destinations.

This doesn't modify the event outside of the specific destination, and failure doesn't halt the execution. | +| `after` | Executes after all event processing completes. You can use this to perform cleanup operations.

An example of this is the [Segment.io Plugin](https://github.com/segmentio/analytics-next/blob/master/packages/browser/src/plugins/segmentio/index.ts){:target="_blank"} which waits for destinations to succeed or fail so it can send it observability metrics. | +| `utility` | Executes once during the bootstrap, to give you an outlet to make any modifications as to how Analytics.js works internally. This allows you to augment Analytics.js functionality. | + +### Example plugins +Here's an example of a plugin that converts all track event names to lowercase before the event goes through the rest of the pipeline: + +```js +export const lowercase: Plugin = { + name: 'Lowercase events', + type: 'enrichment', + version: '1.0.0', + + isLoaded: () => true, + load: () => Promise.resolve(), + + track: (ctx) => { + ctx.updateEvent('event', ctx.event.event.toLowerCase()) + return ctx + } +} + +const identityStitching = () => { + let user + + const identity = { + // Identifies your plugin in the Plugins stack. + // Access `window.analytics.queue.plugins` to see the full list of plugins + name: 'Identity Stitching', + // Defines where in the event timeline a plugin should run + type: 'enrichment', + version: '0.1.0', + + // use the `load` hook to bootstrap your plugin + // The load hook will receive a context object as its first argument + // followed by a reference to the analytics.js instance from the page + load: async (_ctx, ajs) => { + user = ajs.user() + }, + + // Used to signal that a plugin has been property loaded + isLoaded: () => user !== undefined, + + // Applies the plugin code to every `identify` call in Analytics.js + // You can override any of the existing types in the Segment Spec. + async identify(ctx) { + // Request some extra info to enrich your `identify` events from + // an external API. + const req = await fetch( + `https://jsonplaceholder.typicode.com/users/${ctx.event.userId}` + ) + const userReq = await req.json() + + // ctx.updateEvent can be used to update deeply nested properties + // in your events. It's a safe way to change events as it'll + // create any missing objects and properties you may require. + ctx.updateEvent('traits.custom', userReq) + user.traits(userReq) + + // Every plugin must return a `ctx` object, so that the event + // timeline can continue processing. + return ctx + }, + } + + return identity +} + +// Registers Segment's new plugin into Analytics.js +await window.analytics.register(identityStitching()) +``` + +Here's an example of a `utility` plugin that allows you to change the format of the anonymous_id cookie: + +```js + +window.analytics.ready(() => { + window.analytics.register({ + name: 'Cookie Compatibility', + version: '0.1.0', + type: 'utility', + load: (_ctx, ajs) => { + const user = ajs.user() + const cookieJar = user.cookies + const cookieSetter = cookieJar.set.bind(cookieJar) + + // blindly convert any values into JSON strings + cookieJar.set = (key, value, opts) => cookieSetter(key, JSON.stringify(value), opts) + + // stringify any existing IDs + user.anonymousId(user.anonymousId()) + user.id(user.id()) + }, + isLoaded: () => true + }) + }) +``` + +You can view Segment's [existing plugins](https://github.com/segmentio/analytics-next/tree/master/src/plugins){:target="_blank"} to see more examples. + +### Register a plugin +Registering plugins enable you to modify your analytics implementation to best fit your needs. You can register a plugin using this: + +```js +// A promise will resolve once the plugins have been successfully loaded into Analytics.js +// You can register multiple plugins at once by using the variable args interface in Analytics.js +await window.analytics.register(pluginA, pluginB, pluginN) ## Development diff --git a/src/connections/sources/catalog/libraries/server/node/quickstart.md b/src/connections/sources/catalog/libraries/server/node/quickstart.md index 508fde4ae9..6064c5a537 100644 --- a/src/connections/sources/catalog/libraries/server/node/quickstart.md +++ b/src/connections/sources/catalog/libraries/server/node/quickstart.md @@ -1,9 +1,12 @@ --- -title: 'Quickstart: Node.js' +title: 'Quickstart: Node.js Classic' redirect_from: '/connections/sources/catalog/libraries/server/node-js/quickstart/' strat: node-js --- +> warning "Deprecation of Analytics Node.js Classic" +> On [date], Segment will end support for Analytics Node.js Classic, which includes versions [1.x.x] and older. Upgrade to Analytics Node.js 2.0. See the [Analytics Node.js 2.0] docs to learn more. + This tutorial will help you start sending data from your Node servers to Segment and any destination, using Segment's Node library. Check out the full documentation for [Analytics Node.js](/docs/connections/sources/catalog/libraries/server/node) to learn more. To get started with Analytics Node.js: From f217f61bc61d104549a62a7578743af996ae7975 Mon Sep 17 00:00:00 2001 From: stayseesong <83784848+stayseesong@users.noreply.github.com> Date: Thu, 5 Jan 2023 14:41:55 -0800 Subject: [PATCH 03/74] Apply suggestions from code review --- .../sources/catalog/libraries/server/node/index.md | 2 +- src/connections/sources/catalog/libraries/server/node/next.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/connections/sources/catalog/libraries/server/node/index.md b/src/connections/sources/catalog/libraries/server/node/index.md index 46ea81e56d..70103f89fb 100644 --- a/src/connections/sources/catalog/libraries/server/node/index.md +++ b/src/connections/sources/catalog/libraries/server/node/index.md @@ -8,7 +8,7 @@ strat: node-js > warning "Deprecation of Analytics Node.js Classic" > On [date], Segment will end support for Analytics Node.js Classic, which includes versions [1.x.x] and older. Upgrade to Analytics Node.js 2.0. See the [Analytics Node.js 2.0] docs to learn more. -Segment's Node.js library lets you record analytics data from your node code. The requests hit our servers, and then Segment routes your data to any destinations you have enabled. +Segment's Node.js library lets you record analytics data from your node code. The requests hit Segment's servers, and then Segment routes your data to any destinations you have enabled. The [Segment Node.js library is open-source](https://github.com/segmentio/analytics-node) on GitHub. diff --git a/src/connections/sources/catalog/libraries/server/node/next.md b/src/connections/sources/catalog/libraries/server/node/next.md index 9e93819bba..09f0096955 100644 --- a/src/connections/sources/catalog/libraries/server/node/next.md +++ b/src/connections/sources/catalog/libraries/server/node/next.md @@ -6,7 +6,7 @@ strat: node-js Segment's Analytics Node.js 2.0 library lets you record analytics data from your node code. The requests hit Segment's servers, and then Segment routes your data to any destinations you have enabled. -The [Segment Analytics Node.js Next library is open-source](https://github.com/segmentio/analytics-next){:target="_blank"} on GitHub. +The [Segment Analytics Node.js Next library is open-source](https://github.com/segmentio/analytics-next/tree/master/packages/node){:target="_blank"} on GitHub. All of Segment's server-side libraries are built for high-performance, so you can use them in your web server controller code. This library uses an internal queue to make `identify` and `track` calls non-blocking and fast. It also batches messages and flushes asynchronously to Segment's servers. @@ -429,7 +429,7 @@ The event emitter interface allows you to track when certain things happen in th ```javascript analytics.on('error', (err) => console.error(err)) -analytics.on('identify', (ctx) => console.log(err)) +analytics.on('identify', (ctx) => console.log(ctx)) analytics.on('track', (ctx) => console.log(ctx)) ``` From 4144486da6dae6176610498a9a35cf702d567aee Mon Sep 17 00:00:00 2001 From: stayseesong Date: Thu, 5 Jan 2023 16:42:42 -0800 Subject: [PATCH 04/74] edits --- .../catalog/libraries/server/node/index.md | 3 +- .../catalog/libraries/server/node/next.md | 149 ++---------------- 2 files changed, 16 insertions(+), 136 deletions(-) diff --git a/src/connections/sources/catalog/libraries/server/node/index.md b/src/connections/sources/catalog/libraries/server/node/index.md index 70103f89fb..b066c664ef 100644 --- a/src/connections/sources/catalog/libraries/server/node/index.md +++ b/src/connections/sources/catalog/libraries/server/node/index.md @@ -3,10 +3,11 @@ title: Analytics for Node.js Classic redirect_from: '/connections/sources/catalog/libraries/server/node-js/' repo: analytics-node strat: node-js +hidden: true --- > warning "Deprecation of Analytics Node.js Classic" -> On [date], Segment will end support for Analytics Node.js Classic, which includes versions [1.x.x] and older. Upgrade to Analytics Node.js 2.0. See the [Analytics Node.js 2.0] docs to learn more. +> On [date], Segment will end support for Analytics Node.js Classic, which includes versions [1.x.x] and older. Upgrade to the new version of Analytics Node.js. See the updated [Analytics Node.js] docs to learn more. Segment's Node.js library lets you record analytics data from your node code. The requests hit Segment's servers, and then Segment routes your data to any destinations you have enabled. diff --git a/src/connections/sources/catalog/libraries/server/node/next.md b/src/connections/sources/catalog/libraries/server/node/next.md index 09f0096955..62b760a6d2 100644 --- a/src/connections/sources/catalog/libraries/server/node/next.md +++ b/src/connections/sources/catalog/libraries/server/node/next.md @@ -41,18 +41,6 @@ Be sure to replace `YOUR_WRITE_KEY` with your actual **Write Key** which you can This will create an instance of `Analytics` that you can use to send data to Segment for your project. The default initialization settings are production-ready and queue 20 messages before sending any requests. In development you might want to use [development settings](/docs/connections/sources/catalog/libraries/server/node/#development). -### Regional configuration -For Business plans with access to [Regional Segment](/docs/guides/regional-segment), you can use the `host` configuration parameter to send data to the desired region: -1. Oregon (Default) — `api.segment.io/v1` -2. Dublin — `events.eu1.segmentapis.com` - -An example of setting the host to the EU endpoint using the Node library is: -```javascript -const analytics = new Analytics({ - ... - host: "https://events.eu1.segmentapis.com" -}); -``` ## Basic tracking methods The basic tracking methods below serve as the building blocks of your Segment tracking. They include [Identify](#identify), [Track](#track), [Page](#page), [Group](#group), and [Alias](#alias). @@ -286,7 +274,7 @@ The `group` call has the following fields: `traits` _dict, optional_ - A dict of traits you know about the group. For a company, they might be things like `name`, `address`, or `phone`. + A dict of traits you know about the group. For a company, they might be things like `name`, `address`, or `phone`. [Learn more about traits](/docs/connections/spec/group/#traits). `context` _dict, optional_ @@ -412,6 +400,19 @@ await analytics.closeAndFlush() console.log(unflushedEvents) // all events that came in after closeAndFlush was called ``` +## Regional configuration +For Business plans with access to [Regional Segment](/docs/guides/regional-segment), you can use the `host` configuration parameter to send data to the desired region: +1. Oregon (Default) — `api.segment.io/v1` +2. Dublin — `events.eu1.segmentapis.com` + +An example of setting the host to the EU endpoint using the Node library is: +```javascript +const analytics = new Analytics({ + ... + host: "https://events.eu1.segmentapis.com" +}); +``` + ## Error handling To keep track of errors, subscribe and log all event delivery errors by running: @@ -486,13 +487,6 @@ const identityStitching = () => { type: 'enrichment', version: '0.1.0', - // use the `load` hook to bootstrap your plugin - // The load hook will receive a context object as its first argument - // followed by a reference to the analytics.js instance from the page - load: async (_ctx, ajs) => { - user = ajs.user() - }, - // Used to signal that a plugin has been property loaded isLoaded: () => user !== undefined, @@ -521,34 +515,6 @@ const identityStitching = () => { return identity } -// Registers Segment's new plugin into Analytics.js -await window.analytics.register(identityStitching()) -``` - -Here's an example of a `utility` plugin that allows you to change the format of the anonymous_id cookie: - -```js - -window.analytics.ready(() => { - window.analytics.register({ - name: 'Cookie Compatibility', - version: '0.1.0', - type: 'utility', - load: (_ctx, ajs) => { - const user = ajs.user() - const cookieJar = user.cookies - const cookieSetter = cookieJar.set.bind(cookieJar) - - // blindly convert any values into JSON strings - cookieJar.set = (key, value, opts) => cookieSetter(key, JSON.stringify(value), opts) - - // stringify any existing IDs - user.anonymousId(user.anonymousId()) - user.id(user.id()) - }, - isLoaded: () => true - }) - }) ``` You can view Segment's [existing plugins](https://github.com/segmentio/analytics-next/tree/master/src/plugins){:target="_blank"} to see more examples. @@ -560,16 +526,8 @@ Registering plugins enable you to modify your analytics implementation to best f // A promise will resolve once the plugins have been successfully loaded into Analytics.js // You can register multiple plugins at once by using the variable args interface in Analytics.js await window.analytics.register(pluginA, pluginB, pluginN) - -## Development - -You can use this initialization during development to make the library flush every time a message is submitted, so that you can be sure your calls are working properly before pushing to production. - -```javascript -var analytics = new Analytics('YOUR_WRITE_KEY', { flushAt: 1 }); ``` - ## Selecting Destinations The `alias`, `group`, `identify`, `page` and `track` calls can all be passed an object of `integrations` that lets you turn certain destinations on or off. By default all destinations are enabled. @@ -650,85 +608,6 @@ analytics.flush(function(err, batch){ }); ``` - - - - - - ## Multiple Clients Different parts of your application may require different types of batching, or even sending to multiple Segment sources. In that case, you can initialize multiple instances of `Analytics` with different settings: From ad10607ed65aeebeb9d296ced23d559706000e29 Mon Sep 17 00:00:00 2001 From: stayseesong Date: Fri, 6 Jan 2023 17:07:47 -0800 Subject: [PATCH 05/74] more edits --- .../catalog/libraries/server/node/classic.md | 484 ++++++++++++++ .../catalog/libraries/server/node/index.md | 611 ++++++++--------- .../catalog/libraries/server/node/next.md | 625 ------------------ 3 files changed, 777 insertions(+), 943 deletions(-) create mode 100644 src/connections/sources/catalog/libraries/server/node/classic.md delete mode 100644 src/connections/sources/catalog/libraries/server/node/next.md diff --git a/src/connections/sources/catalog/libraries/server/node/classic.md b/src/connections/sources/catalog/libraries/server/node/classic.md new file mode 100644 index 0000000000..bbe4075540 --- /dev/null +++ b/src/connections/sources/catalog/libraries/server/node/classic.md @@ -0,0 +1,484 @@ +--- +title: Analytics for Node.js Classic +repo: analytics-node +strat: node-js +hidden: true +--- + +> warning "Deprecation of Analytics Node.js Classic" +> On [date], Segment will end support for Analytics Node.js Classic, which includes versions [1.x.x] and older. Upgrade to the new version of Analytics Node.js. See the updated [Analytics Node.js] docs to learn more. + +Segment's Node.js library lets you record analytics data from your node code. The requests hit Segment's servers, and then Segment routes your data to any destinations you have enabled. + +The [Segment Node.js library is open-source](https://github.com/segmentio/analytics-node) on GitHub. + +All of Segment's server-side libraries are built for high-performance, so you can use them in your web server controller code. This library uses an internal queue to make `identify` and `track` calls non-blocking and fast. It also batches messages and flushes asynchronously to our servers. + +Want to stay updated on releases? Subscribe to the [release feed](https://github.com/segmentio/analytics-node/releases.atom). + +## Getting Started + +Run: + +```bash +npm install --save analytics-node +``` + +This will add our Node library module to your `package.json`. The module exposes an `Analytics` constructor, which you need to initialize with your Segment source's **Write Key**, like so: + +```javascript +var Analytics = require('analytics-node'); +var analytics = new Analytics('YOUR_WRITE_KEY'); +``` + +Of course, you'll want to replace `YOUR_WRITE_KEY` with your actual **Write Key** which you can find in Segment under your source settings. + +This will create an instance of `Analytics` that you can use to send data to Segment for your project. The default initialization settings are production-ready and queue 20 messages before sending any requests. In development you might want to use [development settings](/docs/connections/sources/catalog/libraries/server/node/#development). + +### Regional configuration +For Business plans with access to [Regional Segment](/docs/guides/regional-segment), you can use the `host` configuration parameter to send data to the desired region: +1. Oregon (Default) — `api.segment.io/v1` +2. Dublin — `events.eu1.segmentapis.com` + +An example of setting the host to the EU endpoint using the Node library would be: +```javascript +var analytics = new Analytics('YOUR_WRITE_KEY', { + host: "https://events.eu1.segmentapis.com" + }); +``` + +## Identify + +> note "" +> **Good to know**: For any of the different methods described on this page, you can replace the properties and traits in the code samples with variables that represent the data collected. + +`identify` lets you tie a user to their actions and record traits about them. It includes a unique User ID and/or anonymous ID, and any optional traits you know about them. + +You should call `identify` once when the user's account is first created, and then again any time their traits change. + +Example of an anonymous `identify` call: + +```javascript +analytics.identify({ + anonymousId: '48d213bb-95c3-4f8d-af97-86b2b404dcfe', + traits: { + friends: 42 + } +}); +``` + +This call identifies the user and records their unique anonymous ID, and labels them with the `friends` trait. + +Example of an `identify` call for an identified user: + +```javascript +analytics.identify({ + userId: '019mr8mf4r', + traits: { + name: 'Michael Bolton', + email: 'mbolton@example.com', + plan: 'Enterprise', + friends: 42 + } +}); +``` +The call above identifies Michael by his unique User ID (the one you know him by in your database), and labels him with the `name`, `email`, `plan` and `friends` traits. + +The `identify` call has the following fields: + +Field | Details +----- | ------- +`userId` _String, optional_ | The ID for this user in your database. _Note: at least one of `userId` or `anonymousId` must be included in any identify call._ +`anonymousId` _String, optional_ | An ID associated with the user when you don't know who they are (for example, [the anonymousId generated by `analytics.js`](/docs/connections/sources/catalog/libraries/website/javascript/#anonymous-id)). _Note: You must include at least one of `userId` or `anonymousId` in all identify calls._ +`traits` _Object, optional_ | A dictionary of [traits](/docs/connections/spec/identify#traits) you know about the user. Things like: `email`, `name` or `friends`. +`timestamp` _Date, optional_ | A JavaScript date object representing when the identify took place. If the identify just happened, leave it out and we'll use the server's time. If you're importing data from the past make sure you to send a `timestamp`. +`context` _Object, optional_ | A dictionary of extra [context](https://segment.com/docs/connections/spec/common/#context) to attach to the call. _Note: `context` differs from `traits` because it is not attributes of the user itself._ + +Find details on the **identify method payload** in the Segment [Spec](/docs/connections/spec/identify/). + +## Track + +`track` lets you record the actions your users perform. Every action triggers what we call an "event", which can also have associated properties. + +You'll want to track events that are indicators of success for your site, like **Signed Up**, **Item Purchased** or **Article Bookmarked**. + +To get started, we recommend tracking just a few important events. You can always add more later! + +Example anonymous `track` call: + +```javascript +analytics.track({ + anonymousId: '48d213bb-95c3-4f8d-af97-86b2b404dcfe', + event: 'Item Purchased', + properties: { + revenue: 39.95, + shippingMethod: '2-day' + } +}); +``` + +Example identified `track` call: + +```javascript +analytics.track({ + userId: '019mr8mf4r', + event: 'Item Purchased', + properties: { + revenue: 39.95, + shippingMethod: '2-day' + } +}); +``` + +This example `track` call tells us that your user just triggered the **Item Purchased** event with a revenue of $39.95 and chose your hypothetical '2-day' shipping. + +`track` event properties can be anything you want to record. In this case, revenue and shipping method. + +The `track` call has the following fields: + +Field | Details +----- | -------- +`userId` _String, optional_ | The ID for this user in your database. _Note: at least one of `userId` or `anonymousId` must be included in any track call. +`anonymousId` _String, optional_ | An ID associated with the user when you don't know who they are (for example, [the anonymousId generated by `analytics.js`](/docs/connections/sources/catalog/libraries/website/javascript/#anonymous-id)). _Note: You must include at least one of `userId` or `anonymousId` in all track calls._ +`event` _String_ | The name of the event you're tracking. We recommend human-readable names like `Song Played` or `Status Updated`. +`properties` _Object, optional_ | A dictionary of properties for the event. If the event was `Product Added`, it might have properties like `price` or `product`. +`timestamp` _Date, optional_ | A JavaScript date object representing when the track took place. If the track just happened, leave it out and we'll use the server's time. If you're importing data from the past make sure you to send a `timestamp`. +`context` _Object, optional_ | A dictionary of extra [context](https://segment.com/docs/connections/spec/common/#context) to attach to the call. _Note: `context` differs from `traits` because it is not attributes of the user itself._ + +Find details on **best practices in event naming** as well as the **`track` method payload** in the Segment [Spec](/docs/connections/spec/track/). + +## Page + +The [`page`](/docs/connections/spec/page/) method lets you record page views on your website, along with optional extra information about the page being viewed. + +If you're using our client-side set up in combination with the Node.js library, page calls are **already tracked for you** by default. However, if you want to record your own page views manually and aren't using our client-side library, read on! + +Example `page` call: + +```js +analytics.page({ + userId: '019mr8mf4r', + category: 'Docs', + name: 'Node.js Library', + properties: { + url: 'https://segment.com/docs/connections/sources/catalog/librariesnode', + path: '/docs/connections/sources/catalog/librariesnode/', + title: 'Node.js Library - Segment', + referrer: 'https://github.com/segmentio/analytics-node' + } +}); +``` + +The `page` call has the following fields: + +Field | Details +----- | -------- +`userId` _String, optional_ | The ID for this user in your database. _Note: at least one of `userId` or `anonymousId` must be included in any page call. +`anonymousId` _String, optional_ | An ID associated with the user when you don't know who they are (eg., [the anonymousId generated by `analytics.js`](/docs/connections/sources/catalog/libraries/website/javascript/#anonymous-id)). _Note: at least one of `userId` or `anonymousId` must be included in any page call._ +`category` _String, optional_ | The category of the page. Useful for things like ecommerce where many pages often live under a larger category. +`name` _String, optional_ | The name of the page, for example **Signup** or **Home**. +`properties` _Object, optional_ | A dictionary of properties of the page. A few properties specially recognized and automatically translated: `url`, `title`, `referrer` and `path`, but you can add your own too. +`timestamp` _Date, optional_ | A JavaScript date object representing when the track took place. If the track just happened, leave it out and we'll use the server's time. If you're importing data from the past make sure you to send a `timestamp`. +`context` _Object, optional_ | A dictionary of extra [context](https://segment.com/docs/connections/spec/common/#context) to attach to the call. _Note: `context` differs from `traits` because it is not attributes of the user itself._ + +Find details on the **`page` payload** in the Segment [Spec](/docs/connections/spec/page/). + +## Group + +`group` lets you associate an [identified user](/docs/connections/sources/catalog/libraries/server/node/#identify) with a group. A group could be a company, organization, account, project or team! It also lets you record custom traits about the group, like industry or number of employees. + +This is useful for tools like [Intercom](/docs/connections/destinations/catalog/intercom/), [Preact](/docs/connections/destinations/catalog/preact/) and [Totango](/docs/connections/destinations/catalog/totango/), as it ties the user to a **group** of other users. + +Example `group` call: + +```javascript +analytics.group({ + userId: '019mr8mf4r', + groupId: '56', + traits: { + name: 'Initech', + description: 'Accounting Software' + } +}); +``` + +The `group` call has the following fields: + +Field | Details +----- | -------- +`userId` _String, optional_ | The ID for this user in your database. _Note: at least one of `userId` or `anonymousId` must be included in any group call. +`anonymousId` _String, optional_ | An ID associated with the user when you don't know who they are (eg., [the anonymousId generated by `analytics.js`](/docs/connections/sources/catalog/libraries/website/javascript/#anonymous-id)). _Note: at least one of `userId` or `anonymousId` must be included in any group call._ +`groupId` _string | The ID of the group. +`traits` _dict, optional_ | A dict of traits you know about the group. For a company, they might be things like `name`, `address`, or `phone`. [Learn more about traits](/docs/connections/spec/group/#traits). +`context` _dict, optional_ | A dict containing any context about the request. To see the full reference of supported keys, check them out in the [context reference](/docs/connections/spec/common/#context) +`timestamp` _datetime, optional_ | A `datetime` object representing when the group took place. If the group just happened, leave it out and we'll use the server's time. If you're importing data from the past make sure you send `timestamp`. +`integrations` _dict, optional_ | A dictionary of destinations to enable or disable. + +Find more details about `group`, including the **`group` payload**, in the Segment [Spec](/docs/connections/spec/group/). + +## Alias + +The `alias` call allows you to associate one identity with another. This is an advanced method and should not be widely used, but is required to manage user identities in _some_ destinations. Other destinations do not support the alias call. + +In [Mixpanel](/docs/connections/destinations/catalog/mixpanel/#alias) it's used to associate an anonymous user with an identified user once they sign up. For [Kissmetrics](/docs/connections/destinations/catalog/kissmetrics/#alias), if your user switches IDs, you can use 'alias' to rename the 'userId'. + +Example `alias` call: + +```javascript +analytics.alias({ + previousId: 'old_id', + userId: 'new_id' +}); +``` + +The `alias` call has the following fields: + +Field | Details +----- | -------- +`userId` _String_ | The ID for this user in your database. +`previousId` _String_ | The previous ID to alias from. + +Here's a full example of how we might use the `alias` call: + +```javascript +// the anonymous user does actions ... +analytics.track({ userId: 'anonymous_user', event: 'Anonymous Event' }) +// the anonymous user signs up and is aliased +analytics.alias({ previousId: 'anonymous_user', userId: 'identified@example.com' }) +// the identified user is identified +analytics.identify({ userId: 'identified@example.com', traits: { plan: 'Free' } }) +// the identified user does actions ... +analytics.track({ userId: 'identified@example.com', event: 'Identified Action' }) +``` + +For more details about `alias`, including the **`alias` call payload**, check out our [Spec](/docs/connections/spec/alias/). + +--- + + +## Configuration + +The second argument to the `Analytics` constructor is an optional dictionary of settings to configure the module. + +```javascript +var analytics = new Analytics('YOUR_WRITE_KEY', { + flushAt: 20, + flushInterval: 10000, + enable: false +}); +``` + +Setting | Details +------- | -------- +`flushAt` _Number_ | The number of messages to enqueue before flushing. +`flushInterval` _Number_ | The number of milliseconds to wait before flushing the queue automatically. +`enable` _Boolean_ | Enable (default) or disable flush. Useful when writing tests and you do not want to send data to Segment Servers. + + +### Error Handling + +Additionally there is an optional `errorHandler` property available to the class constructor's options. +If unspecified, the behaviour of the library does not change. +If specified, when an axios request fails, `errorHandler(axiosError)` will be called instead of re-throwing the axios error. + +Example usage: +```javascript +const Analytics = require('analytics-node'); + +const client = new Analytics('write key', { + errorHandler: (err) => { + console.error('analytics-node flush failed.') + console.error(err) + } +}); + +client.track({ + event: 'event name', + userId: 'user id' +}); + +``` +If this fails when flushed no exception will be thrown, instead the axios error will be logged to the console. + +## Development + +You can use this initialization during development to make the library flush every time a message is submitted, so that you can be sure your calls are working properly before pushing to production. + +```javascript +var analytics = new Analytics('YOUR_WRITE_KEY', { flushAt: 1 }); +``` + + +## Selecting Destinations + +The `alias`, `group`, `identify`, `page` and `track` calls can all be passed an object of `integrations` that lets you turn certain destinations on or off. By default all destinations are enabled. + +Here's an example with the `integrations` object shown: + +```javascript +analytics.track({ + event: 'Membership Upgraded', + userId: '97234974', + integrations: { + 'All': false, + 'Vero': true, + 'Google Analytics': false + } +}) +``` + +In this case, we're specifying that we want this `track` to only go to Vero. `All: false` says that no destination should be enabled unless otherwise specified. `Vero: true` turns on Vero, etc. + +Destination flags are **case sensitive** and match [the destination's name in the docs](/docs/connections/destinations/) (i.e. "AdLearn Open Platform", "awe.sm", "MailChimp", etc.). In some cases, there may be several names for a destination; if that happens you'll see a "Adding (destination name) to the Integrations Object" section in the destination's doc page with a list of valid names. + +**Note:** + +- Available at the business level, filtering track calls can be done right from the Segment UI on your source schema page. We recommend using the UI if possible since it's a much simpler way of managing your filters and can be updated with no code changes on your side. + +- If you are on a grandfathered plan, events sent server-side that are filtered through the Segment dashboard will still count towards your API usage. + +## Historical Import + +You can import historical data by adding the `timestamp` argument to any of your method calls. This can be helpful if you've just switched to Segment. + +Historical imports can only be done into destinations that can accept historical timestamped data. Most analytics tools like Mixpanel, Amplitude, Kissmetrics, etc. can handle that type of data just fine. One common destination that does not accept historical data is Google Analytics since their API cannot accept historical data. + +**Note:** If you're tracking things that are happening right now, leave out the `timestamp` and our servers will timestamp the requests for you. + + +## Batching + +Our libraries are built to support high performance environments. That means it is safe to use our Node library on a web server that's serving hundreds of requests per second. + +Every method you call **does not** result in an HTTP request, but is queued in memory instead. Messages are then flushed in batch in the background, which allows for much faster operation. + +By default, our library will flush: + + - The very first time it gets a message. + - Every 20 messages (controlled by `options.flushAt`). + - If 10 seconds has passed since the last flush (controlled by `options.flushInterval`) + +There is a maximum of `500KB` per batch request and `32KB` per call. + +If you don't want to batch messages, you can turn batching off by setting the `flushAt` option to `1`, like so: + +```javascript +var analytics = new Analytics('YOUR_WRITE_KEY', { flushAt: 1 }); +``` + +Batching means that your message might not get sent right away. But every method call takes an optional `callback`, which you can use to know when a particular message is flushed from the queue, like so: + +```javascript +analytics.track({ + userId: '019mr8mf4r', + event: 'Ultimate Played' +}, function(err, batch){ + if (err) // There was an error flushing your message... + // Your message was successfully flushed! +}); +``` + +You can also flush on demand. For example, at the end of your program, you need to flush to make sure that nothing is left in the queue. To do that, call the `flush` method: + +```javascript +analytics.flush(function(err, batch){ + console.log('Flushed, and now this program can exit!'); +}); +``` + +## Long running process + +You should call `client.track(...)` and know that events will be queued and eventually sent to Segment. To prevent losing messages, be sure to capture any interruption (for example, a server restart) and call flush to know of and delay the process shutdown. + +```js +import { randomUUID } from 'crypto'; +import Analytics from 'analytics-node' + +const WRITE_KEY = '...'; + +const analytics = new Analytics(WRITE_KEY, { flushAt: 10 }); + +analytics.track({ + anonymousId: randomUUID(), + event: 'Test event', + properties: { + name: 'Test event', + timestamp: new Date() + } +}); + +const exitGracefully = async (code) => { + console.log('Flushing events'); + await analytics.flush(function(err, batch) { + console.log('Flushed, and now this program can exit!'); + process.exit(code); + }); +}; + +[ + 'beforeExit', 'uncaughtException', 'unhandledRejection', + 'SIGHUP', 'SIGINT', 'SIGQUIT', 'SIGILL', 'SIGTRAP', + 'SIGABRT','SIGBUS', 'SIGFPE', 'SIGUSR1', 'SIGSEGV', + 'SIGUSR2', 'SIGTERM', +].forEach(evt => process.on(evt, exitGracefully)); + +function logEvery2Seconds(i) { + setTimeout(() => { + console.log('Infinite Loop Test n:', i); + logEvery2Seconds(++i); + }, 2000); +} + +logEvery2Seconds(0); +``` + +## Short lived process + +Short-lived functions have a predictably short and linear lifecycle, so use a queue big enough to hold all messages and then await flush to complete its work. + + +```js +import { randomUUID } from 'crypto'; +import Analytics from 'analytics-node' + + +async function lambda() +{ + const WRITE_KEY = '...'; + const analytics = new Analytics(WRITE_KEY, { flushAt: 20 }); + analytics.flushed = true; + + analytics.track({ + anonymousId: randomUUID(), + event: 'Test event', + properties: { + name: 'Test event', + timestamp: new Date() + } + }); + await analytics.flush(function(err, batch) { + console.log('Flushed, and now this program can exit!'); + }); +} + +lambda(); +``` + + +## Multiple Clients + +Different parts of your application may require different types of batching, or even sending to multiple Segment sources. In that case, you can initialize multiple instances of `Analytics` with different settings: + +```javascript +var Analytics = require('analytics-node'); +var marketingAnalytics = new Analytics('MARKETING_WRITE_KEY'); +var appAnalytics = new Analytics('APP_WRITE_KEY'); +``` + + +## Troubleshooting + +{% include content/troubleshooting-intro.md %} +{% include content/troubleshooting-server-debugger.md %} +{% include content/troubleshooting-server-integration.md %} + diff --git a/src/connections/sources/catalog/libraries/server/node/index.md b/src/connections/sources/catalog/libraries/server/node/index.md index b066c664ef..ff4141c812 100644 --- a/src/connections/sources/catalog/libraries/server/node/index.md +++ b/src/connections/sources/catalog/libraries/server/node/index.md @@ -1,57 +1,62 @@ --- -title: Analytics for Node.js Classic +title: Analytics for Node.js redirect_from: '/connections/sources/catalog/libraries/server/node-js/' repo: analytics-node strat: node-js -hidden: true --- -> warning "Deprecation of Analytics Node.js Classic" -> On [date], Segment will end support for Analytics Node.js Classic, which includes versions [1.x.x] and older. Upgrade to the new version of Analytics Node.js. See the updated [Analytics Node.js] docs to learn more. +Segment's Analytics Node.js 2.0 library lets you record analytics data from your node code. The requests hit Segment's servers, and then Segment routes your data to any destinations you have enabled. -Segment's Node.js library lets you record analytics data from your node code. The requests hit Segment's servers, and then Segment routes your data to any destinations you have enabled. +The [Segment Analytics Node.js Next library is open-source](https://github.com/segmentio/analytics-next/tree/master/packages/node){:target="_blank"} on GitHub. -The [Segment Node.js library is open-source](https://github.com/segmentio/analytics-node) on GitHub. +All of Segment's server-side libraries are built for high-performance, so you can use them in your web server controller code. This library uses an internal queue to make `identify` and `track` calls non-blocking and fast. It also batches messages and flushes asynchronously to Segment's servers. -All of Segment's server-side libraries are built for high-performance, so you can use them in your web server controller code. This library uses an internal queue to make `identify` and `track` calls non-blocking and fast. It also batches messages and flushes asynchronously to our servers. - -Want to stay updated on releases? Subscribe to the [release feed](https://github.com/segmentio/analytics-node/releases.atom). +> info "Using Analytics for Node.js Classic?" +> If you’re still using the classic version of Analytics for Node.js, you can refer to the documentation [here](/docs/connections/sources/catalog/libraries/server/node/classic). +>

On [date], Segment will end support for Analytics Node.js Classic, which includes versions [1.x.x] and older. Upgrade to Analytics Node.js 2.0. See the Analytics Node.js 2.0 docs to learn more. ## Getting Started -Run: +> warning "" +> Make sure you're using a version of Node that's 14 or higher. -```bash -npm install --save analytics-node -``` +1. Run the following to add Segment's Node library module to your `package.json`. -This will add our Node library module to your `package.json`. The module exposes an `Analytics` constructor, which you need to initialize with your Segment source's **Write Key**, like so: + ```bash + # npm + npm install @segment/analytics-node + # yarn + yarn add @segment/analytics-node + # pnpm + pnpm install @segment/analytics-node + ``` -```javascript -var Analytics = require('analytics-node'); -var analytics = new Analytics('YOUR_WRITE_KEY'); -``` +2. Initialize the `Analytics` constructor the module exposes with your Segment source **Write Key**, like so: -Of course, you'll want to replace `YOUR_WRITE_KEY` with your actual **Write Key** which you can find in Segment under your source settings. + ```javascript + import { Analytics } from '@segment/analytics-node' + // or, if you use require: + const { Analytics } = require('@segment/analytics-node') -This will create an instance of `Analytics` that you can use to send data to Segment for your project. The default initialization settings are production-ready and queue 20 messages before sending any requests. In development you might want to use [development settings](/docs/connections/sources/catalog/libraries/server/node/#development). + // instantiation + const analytics = new Analytics({ writeKey: '' }) + ``` -### Regional configuration -For Business plans with access to [Regional Segment](/docs/guides/regional-segment), you can use the `host` configuration parameter to send data to the desired region: -1. Oregon (Default) — `api.segment.io/v1` -2. Dublin — `events.eu1.segmentapis.com` + Be sure to replace `YOUR_WRITE_KEY` with your actual **Write Key** which you can find in Segment under your source settings. -An example of setting the host to the EU endpoint using the Node library would be: -```javascript -var analytics = new Analytics('YOUR_WRITE_KEY', { - host: "https://events.eu1.segmentapis.com" - }); -``` + This creates an instance of `Analytics` that you can use to send data to Segment for your project. The default initialization settings are production-ready and queue 20 messages before sending any requests. In development you might want to use [development settings](/docs/connections/sources/catalog/libraries/server/node/#development). + + +## Basic tracking methods +The basic tracking methods below serve as the building blocks of your Segment tracking. They include [Identify](#identify), [Track](#track), [Page](#page), [Group](#group), and [Alias](#alias). -## Identify +These methods correspond with those used in the [Segment Spec](/docs/connections/spec/). The documentation on this page explains how to use these methods in Analytics Node.js Next. -> note "" -> **Good to know**: For any of the different methods described on this page, you can replace the properties and traits in the code samples with variables that represent the data collected. + +### Identify + +> info "Good to know" +> For any of the different methods described on this page, you can replace the properties and traits in the code samples with variables that represent the data collected. `identify` lets you tie a user to their actions and record traits about them. It includes a unique User ID and/or anonymous ID, and any optional traits you know about them. @@ -87,32 +92,17 @@ The call above identifies Michael by his unique User ID (the one you know him by The `identify` call has the following fields: - - - - - - - - - - - - - - - - - - - - - -
`userId` _String, optional_The ID for this user in your database. _Note: at least one of `userId` or `anonymousId` must be included in any identify call._
`anonymousId` _String, optional_An ID associated with the user when you don't know who they are (for example, [the anonymousId generated by `analytics.js`](/docs/connections/sources/catalog/libraries/website/javascript/#anonymous-id)). _Note: You must include at least one of `userId` or `anonymousId` in all identify calls._
`traits` _Object, optional_A dictionary of [traits](/docs/connections/spec/identify#traits) you know about the user. Things like: `email`, `name` or `friends`.
`timestamp` _Date, optional_A JavaScript date object representing when the identify took place. If the identify just happened, leave it out and we'll use the server's time. If you're importing data from the past make sure you to send a `timestamp`.
`context` _Object, optional_A dictionary of extra [context](https://segment.com/docs/connections/spec/common/#context) to attach to the call. _Note: `context` differs from `traits` because it is not attributes of the user itself._
- -Find details on the **identify method payload** in our [Spec](/docs/connections/spec/identify/). - -## Track +Field | Details +----- | ------- +`userId` _String, optional_ | The ID for this user in your database. _Note: at least one of `userId` or `anonymousId` must be included in any identify call._ +`anonymousId` _String, optional_ | An ID associated with the user when you don't know who they are (for example, [the anonymousId generated by `analytics.js`](/docs/connections/sources/catalog/libraries/website/javascript/#anonymous-id)). _Note: You must include at least one of `userId` or `anonymousId` in all identify calls._ +`traits` _Object, optional_ | A dictionary of [traits](/docs/connections/spec/identify#traits) you know about the user. Things like: `email`, `name` or `friends`. +`timestamp` _Date, optional_ | A JavaScript date object representing when the identify took place. If the identify just happened, leave it out and we'll use the server's time. If you're importing data from the past make sure you to send a `timestamp`. +`context` _Object, optional_ | A dictionary of extra [context](https://segment.com/docs/connections/spec/common/#context) to attach to the call. _Note: `context` differs from `traits` because it is not attributes of the user itself._ + +Find details on the **identify method payload** in Segment's [Spec](/docs/connections/spec/identify/). + +### Track `track` lets you record the actions your users perform. Every action triggers what we call an "event", which can also have associated properties. @@ -152,36 +142,18 @@ This example `track` call tells us that your user just triggered the **Item Purc The `track` call has the following fields: - - - - - - - - - - - - - - - - - - - - - - - - - -
`userId` _String, optional_The ID for this user in your database. _Note: at least one of `userId` or `anonymousId` must be included in any track call._
`anonymousId` _String, optional_An ID associated with the user when you don't know who they are (for example, [the anonymousId generated by `analytics.js`](/docs/connections/sources/catalog/libraries/website/javascript/#anonymous-id)). _Note: You must include at least one of `userId` or `anonymousId` in all track calls._
`event` _String_The name of the event you're tracking. We recommend human-readable names like `Song Played` or `Status Updated`.
`properties` _Object, optional_A dictionary of properties for the event. If the event was `Product Added`, it might have properties like `price` or `product`.
`timestamp` _Date, optional_A JavaScript date object representing when the track took place. If the track just happened, leave it out and we'll use the server's time. If you're importing data from the past make sure you to send a `timestamp`.
`context` _Object, optional_A dictionary of extra [context](https://segment.com/docs/connections/spec/common/#context) to attach to the call. _Note: `context` differs from `traits` because it is not attributes of the user itself._
- -Find details on **best practices in event naming** as well as the **`track` method payload** in our [Spec](/docs/connections/spec/track/). - -## Page +Field | Details +----- | -------- +`userId` _String, optional_ | The ID for this user in your database. _Note: at least one of `userId` or `anonymousId` must be included in any track call. +`anonymousId` _String, optional_ | An ID associated with the user when you don't know who they are (for example, [the anonymousId generated by `analytics.js`](/docs/connections/sources/catalog/libraries/website/javascript/#anonymous-id)). _Note: You must include at least one of `userId` or `anonymousId` in all track calls._ +`event` _String_ | The name of the event you're tracking. We recommend human-readable names like `Song Played` or `Status Updated`. +`properties` _Object, optional_ | A dictionary of properties for the event. If the event was `Product Added`, it might have properties like `price` or `product`. +`timestamp` _Date, optional_ | A JavaScript date object representing when the track took place. If the track just happened, leave it out and we'll use the server's time. If you're importing data from the past make sure you to send a `timestamp`. +`context` _Object, optional_ | A dictionary of extra [context](https://segment.com/docs/connections/spec/common/#context) to attach to the call. _Note: `context` differs from `traits` because it is not attributes of the user itself._ + +Find details on **best practices in event naming** as well as the **`track` method payload** in the Segment [Spec](/docs/connections/spec/track/). + +### Page The [`page`](/docs/connections/spec/page/) method lets you record page views on your website, along with optional extra information about the page being viewed. @@ -205,40 +177,19 @@ analytics.page({ The `page` call has the following fields: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
`userId` _String, optional_The ID for this user in your database. _Note: at least one of `userId` or `anonymousId` must be included in any page call._
`anonymousId` _String, optional_An ID associated with the user when you don't know who they are (eg., [the anonymousId generated by `analytics.js`](/docs/connections/sources/catalog/libraries/website/javascript/#anonymous-id)). _Note: at least one of `userId` or `anonymousId` must be included in any page call._
`category` _String, optional_The category of the page. Useful for things like ecommerce where many pages often live under a larger category.
`name` _String, optional_The name of the page, for example **Signup** or **Home**.
`properties` _Object, optional_A dictionary of properties of the page. A few properties specially recognized and automatically translated: `url`, `title`, `referrer` and `path`, but you can add your own too!
`timestamp` _Date, optional_A JavaScript date object representing when the track took place. If the track just happened, leave it out and we'll use the server's time. If you're importing data from the past make sure you to send a `timestamp`.
`context` _Object, optional_A dictionary of extra [context](https://segment.com/docs/connections/spec/common/#context) to attach to the call. _Note: `context` differs from `traits` because it is not attributes of the user itself._
- -Find details on the **`page` payload** in our [Spec](/docs/connections/spec/page/). - -## Group +Field | Details +----- | -------- +`userId` _String, optional_ | The ID for this user in your database. _Note: at least one of `userId` or `anonymousId` must be included in any page call. +`anonymousId` _String, optional_ | An ID associated with the user when you don't know who they are (eg., [the anonymousId generated by `analytics.js`](/docs/connections/sources/catalog/libraries/website/javascript/#anonymous-id)). _Note: at least one of `userId` or `anonymousId` must be included in any page call._ +`category` _String, optional_ | The category of the page. Useful for things like ecommerce where many pages often live under a larger category. +`name` _String, optional_ | The name of the page, for example **Signup** or **Home**. +`properties` _Object, optional_ | A dictionary of properties of the page. A few properties specially recognized and automatically translated: `url`, `title`, `referrer` and `path`, but you can add your own too. +`timestamp` _Date, optional_ | A JavaScript date object representing when the track took place. If the track just happened, leave it out and we'll use the server's time. If you're importing data from the past make sure you to send a `timestamp`. +`context` _Object, optional_ | A dictionary of extra [context](https://segment.com/docs/connections/spec/common/#context) to attach to the call. _Note: `context` differs from `traits` because it is not attributes of the user itself._ + +Find details on the **`page` payload** in the Segment [Spec](/docs/connections/spec/page/). + +### Group `group` lets you associate an [identified user](/docs/connections/sources/catalog/libraries/server/node/#identify) with a group. A group could be a company, organization, account, project or team! It also lets you record custom traits about the group, like industry or number of employees. @@ -259,40 +210,19 @@ analytics.group({ The `group` call has the following fields: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
`userId` _String, optional_The ID for this user in your database. _Note: at least one of `userId` or `anonymousId` must be included in any group call._
`anonymousId` _String, optional_An ID associated with the user when you don't know who they are (eg., [the anonymousId generated by `analytics.js`](/docs/connections/sources/catalog/libraries/website/javascript/#anonymous-id)). _Note: at least one of `userId` or `anonymousId` must be included in any group call._
`groupId` _stringThe ID of the group.
`traits` _dict, optional_A dict of traits you know about the group. For a company, they might be things like `name`, `address`, or `phone`.
`context` _dict, optional_A dict containing any context about the request. To see the full reference of supported keys, check them out in the [context reference](/docs/connections/spec/common/#context)
`timestamp` _datetime, optional_A `datetime` object representing when the group took place. If the group just happened, leave it out and we'll use the server's time. If you're importing data from the past make sure you send `timestamp`.
`integrations` _dict, optional_A dictionary of destinations to enable or disable
- -Find more details about `group`, including the **`group` payload**, in our [Spec](/docs/connections/spec/group/). - -## Alias +Field | Details +----- | -------- +`userId` _String, optional_ | The ID for this user in your database. _Note: at least one of `userId` or `anonymousId` must be included in any group call. +`anonymousId` _String, optional_ | An ID associated with the user when you don't know who they are (eg., [the anonymousId generated by `analytics.js`](/docs/connections/sources/catalog/libraries/website/javascript/#anonymous-id)). _Note: at least one of `userId` or `anonymousId` must be included in any group call._ +`groupId` _string | The ID of the group. +`traits` _dict, optional_ | A dict of traits you know about the group. For a company, they might be things like `name`, `address`, or `phone`. [Learn more about traits](/docs/connections/spec/group/#traits). +`context` _dict, optional_ | A dict containing any context about the request. To see the full reference of supported keys, check them out in the [context reference](/docs/connections/spec/common/#context) +`timestamp` _datetime, optional_ | A `datetime` object representing when the group took place. If the group just happened, leave it out and we'll use the server's time. If you're importing data from the past make sure you send `timestamp`. +`integrations` _dict, optional_ | A dictionary of destinations to enable or disable. + +Find more details about `group`, including the **`group` payload**, in the Segment [Spec](/docs/connections/spec/group/). + +### Alias The `alias` call allows you to associate one identity with another. This is an advanced method and should not be widely used, but is required to manage user identities in _some_ destinations. Other destinations do not support the alias call. @@ -309,18 +239,12 @@ analytics.alias({ The `alias` call has the following fields: - - - - - - - - - -
`userId` _String_The ID for this user in your database.
`previousId` _String_The previous ID to alias from.
+Field | Details +----- | -------- +`userId` _String_ | The ID for this user in your database. +`previousId` _String_ | The previous ID to alias from. -Here's a full example of how we might use the `alias` call: +Here's a full example of how Segment might use the `alias` call: ```javascript // the anonymous user does actions ... @@ -333,71 +257,207 @@ analytics.identify({ userId: 'identified@example.com', traits: { plan: 'Free' } analytics.track({ userId: 'identified@example.com', event: 'Identified Action' }) ``` -For more details about `alias`, including the **`alias` call payload**, check out our [Spec](/docs/connections/spec/alias/). +For more details about `alias`, including the **`alias` call payload**, check out the Segment [Spec](/docs/connections/spec/alias/). --- ## Configuration -The second argument to the `Analytics` constructor is an optional dictionary of settings to configure the module. +The second argument to the `Analytics` constructor is an optional list of settings to configure the module. ```javascript -var analytics = new Analytics('YOUR_WRITE_KEY', { - flushAt: 20, - flushInterval: 10000, - enable: false -}); +const analytics = new Analytics({ + writeKey: '', + host: 'https://api.segment.io', + path: '/v1/batch', + maxRetries: 3, + maxEventsInBatch: 15, + flushInterval: 10000, + // ... and more! + }) ``` - - - - - - - - - - - - - -
`flushAt` _Number_The number of messages to enqueue before flushing.
`flushInterval` _Number_The number of milliseconds to wait before flushing the queue automatically.
`enable` _Boolean_Enable (default) or disable flush. Useful when writing tests and you do not want to send data to Segment Servers.
- -### Error Handling - -Additionally there is an optional `errorHandler` property available to the class constructor's options. -If unspecified, the behaviour of the library does not change. -If specified, when an axios request fails, `errorHandler(axiosError)` will be called instead of re-throwing the axios error. - -Example usage: +Setting | Details +------- | -------- +`writeKey` _string_ | The key that corresponds to your Segment.io project +`host` _string_ | The base URL of the API. The default is: "https://api.segment.io" +`path` _string_ | The API path route. The default is: "/v1/batch" +`maxRetries` _number_ | The number of times to retry flushing a batch. The default is: `3` +`maxEventsInBatch` _number_ | The number of messages to enqueue before flushing. The default is: `15` +`flushInterval` _number_ | The number of milliseconds to wait before flushing the queue automatically. The default is: `10000` +`httpRequestTimeout` | The maximum number of milliseconds to wait for an http request. The default is: `10000` + +### Graceful shutdown +Avoid losing events after shutting down your console. Call `.closeAndFlush()` to stop collecting new events and flush all existing events. If a callback on an event call is included, this also waits for all callbacks to be called, and any of their subsequent promises to be resolved. + ```javascript -const Analytics = require('analytics-node'); +await analytics.closeAndFlush() +// or +await analytics.closeAndFlush({ timeout: 5000 }) // force resolve after 5000ms +``` -const client = new Analytics('write key', { - errorHandler: (err) => { - console.error('analytics-node flush failed.') - console.error(err) - } -}); +Here's an example of how to use graceful shutdown: +```javascript +const app = express() +const server = app.listen(3000) + +const onExit = async () => { + await analytics.closeAndFlush() + server.close(() => { + console.log("Gracefully closing server...") + process.exit() + }) +} +['SIGINT', 'SIGTERM'].forEach((code) => process.on(code, onExit)) +``` + +### Collect unflushed events +If you need to preserve all of your events in the instance of a forced timeout, even ones that came in after analytics.closeAndFlush() was called, you can still collect those events by using: + +```javascript +const unflushedEvents = [] + +analytics.on('call_after_close', (event) => unflushedEvents.push(events)) +await analytics.closeAndFlush() + +console.log(unflushedEvents) // all events that came in after closeAndFlush was called +``` + +## Regional configuration +For Business plans with access to [Regional Segment](/docs/guides/regional-segment), you can use the `host` configuration parameter to send data to the desired region: +1. Oregon (Default) — `api.segment.io/v1` +2. Dublin — `events.eu1.segmentapis.com` -client.track({ - event: 'event name', - userId: 'user id' +An example of setting the host to the EU endpoint using the Node library is: +```javascript +const analytics = new Analytics({ + ... + host: "https://events.eu1.segmentapis.com" }); +``` + +## Error handling + +To keep track of errors, subscribe and log all event delivery errors by running: +```javascript +const analytics = new Analytics({ writeKey: '' }) + +analytics.on('error', (err) => console.error(err)) ``` -If this fails when flushed no exception will be thrown, instead the axios error will be logged to the console. -## Development -You can use this initialization during development to make the library flush every time a message is submitted, so that you can be sure your calls are working properly before pushing to production. +### Event emitter interface +The event emitter interface allows you to track when certain things happen in the app, such as a track call or an error, and it will call the function you provided with some arguments when that event happens. ```javascript -var analytics = new Analytics('YOUR_WRITE_KEY', { flushAt: 1 }); +analytics.on('error', (err) => console.error(err)) + +analytics.on('identify', (ctx) => console.log(ctx)) + +analytics.on('track', (ctx) => console.log(ctx)) +``` + +## Plugin architecture +When you develop against Analytics 2.0, the plugins you write can augment functionality, enrich data, and control the flow and delivery of events. From modifying event payloads to changing analytics functionality, plugins help to speed up the process of getting things done. + +Though middlewares function the same as plugins, it's best to use plugins as they are easier to implement and are more testable. + +### Plugin categories +Plugins are bound by Analytics 2.0 which handles operations such as observability, retries, and error handling. There are two different categories of plugins: +* **Critical Plugins**: Analytics.js expects this plugin to be loaded before starting event delivery. Failure to load a critical plugin halts event delivery. Use this category sparingly, and only for plugins that are critical to your tracking. +* **Non-critical Plugins**: Analytics.js can start event delivery before this plugin finishes loading. This means your plugin can fail to load independently from all other plugins. For example, every Analytics.js destination is a non-critical plugin. This makes it possible for Analytics.js to continue working if a partner destination fails to load, or if users have ad blockers turned on that are targeting specific destinations. + +> info "" +> Non-critical plugins are only non-critical from a loading standpoint. For example, if the `before` plugin crashes, this can still halt the event delivery pipeline. + +Non-critical plugins run through a timeline that executes in order of insertion based on the entry type. Segment has these five entry types of non-critical plugins: + +| Type | Details | +| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `before` | Executes before event processing begins. These are plugins that run before any other plugins run.

For example, validating events before passing them along to other plugins. A failure here could halt the event pipeline.

See the example of how Analytics.js uses the [Event Validation plugin](https://github.com/segmentio/analytics-next/blob/master/packages/browser/src/plugins/validation/index.ts){:target="_blank"} to verify that every event has the correct shape. | +| `enrichment` | Executes as the first level of event processing. These plugins modify an event.

See the example of how Analytics.js uses the [Page Enrichment plugin](https://github.com/segmentio/analytics-next/blob/master/packages/browser/src/plugins/page-enrichment/index.ts){:target="_blank"} to enrich every event with page information. | +| `destination` | Executes as events begin to pass off to destinations.

This doesn't modify the event outside of the specific destination, and failure doesn't halt the execution. | +| `after` | Executes after all event processing completes. You can use this to perform cleanup operations.

An example of this is the [Segment.io Plugin](https://github.com/segmentio/analytics-next/blob/master/packages/browser/src/plugins/segmentio/index.ts){:target="_blank"} which waits for destinations to succeed or fail so it can send it observability metrics. | +| `utility` | Executes once during the bootstrap, to give you an outlet to make any modifications as to how Analytics.js works internally. This allows you to augment Analytics.js functionality. | + +### Example plugins +Here's an example of a plugin that converts all track event names to lowercase before the event goes through the rest of the pipeline: + +```js +export const lowercase: Plugin = { + name: 'Lowercase events', + type: 'enrichment', + version: '1.0.0', + + isLoaded: () => true, + load: () => Promise.resolve(), + + track: (ctx) => { + ctx.updateEvent('event', ctx.event.event.toLowerCase()) + return ctx + } +} + +const identityStitching = () => { + let user + + const identity = { + // Identifies your plugin in the Plugins stack. + // Access `window.analytics.queue.plugins` to see the full list of plugins + name: 'Identity Stitching', + // Defines where in the event timeline a plugin should run + type: 'enrichment', + version: '0.1.0', + + // Used to signal that a plugin has been property loaded + isLoaded: () => user !== undefined, + + // Applies the plugin code to every `identify` call in Analytics.js + // You can override any of the existing types in the Segment Spec. + async identify(ctx) { + // Request some extra info to enrich your `identify` events from + // an external API. + const req = await fetch( + `https://jsonplaceholder.typicode.com/users/${ctx.event.userId}` + ) + const userReq = await req.json() + + // ctx.updateEvent can be used to update deeply nested properties + // in your events. It's a safe way to change events as it'll + // create any missing objects and properties you may require. + ctx.updateEvent('traits.custom', userReq) + user.traits(userReq) + + // Every plugin must return a `ctx` object, so that the event + // timeline can continue processing. + return ctx + }, + } + + return identity +} + ``` +You can view Segment's [existing plugins](https://github.com/segmentio/analytics-next/tree/master/src/plugins){:target="_blank"} to see more examples. + +### Register a plugin +Registering plugins enable you to modify your analytics implementation to best fit your needs. You can register a plugin using this: + +```js +// A promise will resolve once the plugins have been successfully loaded into Analytics.js +// Register multiple plugins at once by using the variable args interface in Analytics.js +await analytics.register(pluginA, pluginB, pluginC) +``` + +### Deregister a plugin +Deregister a plugin by using: + +```js +await analytics.dereigsrer("pluginNameA", "pluginNameB") // takes strings +``` ## Selecting Destinations @@ -417,9 +477,9 @@ analytics.track({ }) ``` -In this case, we're specifying that we want this `track` to only go to Vero. `All: false` says that no destination should be enabled unless otherwise specified. `Vero: true` turns on Vero, etc. +In this case, Segment specifies that they want this `track` to only go to Vero. `All: false` says that no destination should be enabled unless otherwise specified. `Vero: true` turns on Vero. -Destination flags are **case sensitive** and match [the destination's name in the docs](/docs/connections/destinations/) (i.e. "AdLearn Open Platform", "awe.sm", "MailChimp", etc.). In some cases, there may be several names for a destination; if that happens you'll see a "Adding (destination name) to the Integrations Object" section in the destination's doc page with a list of valid names. +Destination flags are **case sensitive** and match [the destination's name in the docs](/docs/connections/destinations/) (for example, "AdLearn Open Platform", "awe.sm", "MailChimp"). In some cases, there may be several names for a destination; if that happens you'll see a "Adding (destination name) to the Integrations Object" section in the destination's doc page with a list of valid names. **Note:** @@ -433,136 +493,51 @@ You can import historical data by adding the `timestamp` argument to any of your Historical imports can only be done into destinations that can accept historical timestamped data. Most analytics tools like Mixpanel, Amplitude, Kissmetrics, etc. can handle that type of data just fine. One common destination that does not accept historical data is Google Analytics since their API cannot accept historical data. -**Note:** If you're tracking things that are happening right now, leave out the `timestamp` and our servers will timestamp the requests for you. +**Note:** If you're tracking things that are happening right now, leave out the `timestamp` and Segment's servers will timestamp the requests for you. ## Batching -Our libraries are built to support high performance environments. That means it is safe to use our Node library on a web server that's serving hundreds of requests per second. +Segment's libraries are built to support high performance environments. That means it is safe to use Segment's Node library on a web server that's serving hundreds of requests per second. -Every method you call **does not** result in an HTTP request, but is queued in memory instead. Messages are then flushed in batch in the background, which allows for much faster operation. +Every method you call **doesn't** result in a HTTP request, but is queued in memory instead. Messages are then flushed in batch in the background, which allows for much faster operation. -By default, our library will flush: +By default, Segment's library will flush: - The very first time it gets a message. - - Every 20 messages (controlled by `options.flushAt`). - - If 10 seconds has passed since the last flush (controlled by `options.flushInterval`) + - Every 15 messages (controlled by `settings.maxEventsInBatch`). + - If 10 seconds has passed since the last flush (controlled by `settings.flushInterval`) There is a maximum of `500KB` per batch request and `32KB` per call. -If you don't want to batch messages, you can turn batching off by setting the `flushAt` option to `1`, like so: +If you don't want to batch messages, you can turn batching off by setting the `maxEventsInBatch` setting to `1`, like so: ```javascript -var analytics = new Analytics('YOUR_WRITE_KEY', { flushAt: 1 }); -``` +const analytics = new Analytics({ + ... + maxEventsInBatch: 1 +});``` -Batching means that your message might not get sent right away. But every method call takes an optional `callback`, which you can use to know when a particular message is flushed from the queue, like so: +Batching means that your message might not get sent right away. Every method call takes an optional `callback`, which you can use to know when a particular message is flushed from the queue, like so: ```javascript analytics.track({ - userId: '019mr8mf4r', - event: 'Ultimate Played' -}, function(err, batch){ - if (err) // There was an error flushing your message... - // Your message was successfully flushed! -}); -``` - -You can also flush on demand. For example, at the end of your program, you need to flush to make sure that nothing is left in the queue. To do that, call the `flush` method: - -```javascript -analytics.flush(function(err, batch){ - console.log('Flushed, and now this program can exit!'); -}); -``` - -## Long running process - -You should call `client.track(...)` and know that events will be queued and eventually sent to Segment. To prevent losing messages, be sure to capture any interruption (for example, a server restart) and call flush to know of and delay the process shutdown. - -```js -import { randomUUID } from 'crypto'; -import Analytics from 'analytics-node' - -const WRITE_KEY = '...'; - -const analytics = new Analytics(WRITE_KEY, { flushAt: 10 }); - -analytics.track({ - anonymousId: randomUUID(), - event: 'Test event', - properties: { - name: 'Test event', - timestamp: new Date() + userId: '019mr8mf4r', + event: 'Ultimate Played', + }, + (err, ctx) => { + ... } -}); - -const exitGracefully = async (code) => { - console.log('Flushing events'); - await analytics.flush(function(err, batch) { - console.log('Flushed, and now this program can exit!'); - process.exit(code); - }); -}; - -[ - 'beforeExit', 'uncaughtException', 'unhandledRejection', - 'SIGHUP', 'SIGINT', 'SIGQUIT', 'SIGILL', 'SIGTRAP', - 'SIGABRT','SIGBUS', 'SIGFPE', 'SIGUSR1', 'SIGSEGV', - 'SIGUSR2', 'SIGTERM', -].forEach(evt => process.on(evt, exitGracefully)); - -function logEvery2Seconds(i) { - setTimeout(() => { - console.log('Infinite Loop Test n:', i); - logEvery2Seconds(++i); - }, 2000); -} - -logEvery2Seconds(0); +) ``` -## Short lived process - -Short-lived functions have a predictably short and linear lifecycle, so use a queue big enough to hold all messages and then await flush to complete its work. - - -```js -import { randomUUID } from 'crypto'; -import Analytics from 'analytics-node' - - -async function lambda() -{ - const WRITE_KEY = '...'; - const analytics = new Analytics(WRITE_KEY, { flushAt: 20 }); - analytics.flushed = true; - - analytics.track({ - anonymousId: randomUUID(), - event: 'Test event', - properties: { - name: 'Test event', - timestamp: new Date() - } - }); - await analytics.flush(function(err, batch) { - console.log('Flushed, and now this program can exit!'); - }); -} - -lambda(); -``` - - ## Multiple Clients Different parts of your application may require different types of batching, or even sending to multiple Segment sources. In that case, you can initialize multiple instances of `Analytics` with different settings: ```javascript -var Analytics = require('analytics-node'); -var marketingAnalytics = new Analytics('MARKETING_WRITE_KEY'); -var appAnalytics = new Analytics('APP_WRITE_KEY'); +const marketingAnalytics = new Analytics({ writeKey: 'MARKETING_WRITE_KEY' }); +const appAnalytics = new Analytics({ writeKey: 'APP_WRITE_KEY' }); ``` diff --git a/src/connections/sources/catalog/libraries/server/node/next.md b/src/connections/sources/catalog/libraries/server/node/next.md deleted file mode 100644 index 62b760a6d2..0000000000 --- a/src/connections/sources/catalog/libraries/server/node/next.md +++ /dev/null @@ -1,625 +0,0 @@ ---- -title: Analytics for Node.js 2.0 -repo: analytics-node -strat: node-js ---- - -Segment's Analytics Node.js 2.0 library lets you record analytics data from your node code. The requests hit Segment's servers, and then Segment routes your data to any destinations you have enabled. - -The [Segment Analytics Node.js Next library is open-source](https://github.com/segmentio/analytics-next/tree/master/packages/node){:target="_blank"} on GitHub. - -All of Segment's server-side libraries are built for high-performance, so you can use them in your web server controller code. This library uses an internal queue to make `identify` and `track` calls non-blocking and fast. It also batches messages and flushes asynchronously to Segment's servers. - -> info "Using Analytics for Node.js Classic?" -> If you’re still using the classic version of Analytics for Node.js, you can refer to the documentation [here](/docs/connections/sources/catalog/libraries/server/node/). ->

On [date], Segment will end support for Analytics Node.js Classic, which includes versions [#] and older. Upgrade to Analytics Node.js 2.0. See the Analytics Node.js 2.0 docs to learn more. - -## Getting Started - -> warning "" -> Make sure you're using a version of Node that's 14 or higher. - -Run: - -```bash -# npm -npm install @segment/analytics-node -# yarn -yarn add @segment/analytics-node -# pnpm -pnpm install @segment/analytics-node -``` - -This will add Segment's Node library module to your `package.json`. The module exposes an `Analytics` constructor, which you need to initialize with your Segment source's **Write Key**, like so: - -```javascript -var Analytics = require('analytics-node'); -var analytics = new Analytics('YOUR_WRITE_KEY'); -``` - -Be sure to replace `YOUR_WRITE_KEY` with your actual **Write Key** which you can find in Segment under your source settings. - -This will create an instance of `Analytics` that you can use to send data to Segment for your project. The default initialization settings are production-ready and queue 20 messages before sending any requests. In development you might want to use [development settings](/docs/connections/sources/catalog/libraries/server/node/#development). - - -## Basic tracking methods -The basic tracking methods below serve as the building blocks of your Segment tracking. They include [Identify](#identify), [Track](#track), [Page](#page), [Group](#group), and [Alias](#alias). - -These methods correspond with those used in the [Segment Spec](/docs/connections/spec/). The documentation on this page explains how to use these methods in Analytics Node.js Next. - - -### Identify - -> info "Good to know" -> For any of the different methods described on this page, you can replace the properties and traits in the code samples with variables that represent the data collected. - -`identify` lets you tie a user to their actions and record traits about them. It includes a unique User ID and/or anonymous ID, and any optional traits you know about them. - -You should call `identify` once when the user's account is first created, and then again any time their traits change. - -Example of an anonymous `identify` call: - -```javascript -analytics.identify({ - anonymousId: '48d213bb-95c3-4f8d-af97-86b2b404dcfe', - traits: { - friends: 42 - } -}); -``` - -This call identifies the user and records their unique anonymous ID, and labels them with the `friends` trait. - -Example of an `identify` call for an identified user: - -```javascript -analytics.identify({ - userId: '019mr8mf4r', - traits: { - name: 'Michael Bolton', - email: 'mbolton@example.com', - plan: 'Enterprise', - friends: 42 - } -}); -``` -The call above identifies Michael by his unique User ID (the one you know him by in your database), and labels him with the `name`, `email`, `plan` and `friends` traits. - -The `identify` call has the following fields: - - - - - - - - - - - - - - - - - - - - - - -
`userId` _String, optional_The ID for this user in your database. _Note: at least one of `userId` or `anonymousId` must be included in any identify call._
`anonymousId` _String, optional_An ID associated with the user when you don't know who they are (for example, [the anonymousId generated by `analytics.js`](/docs/connections/sources/catalog/libraries/website/javascript/#anonymous-id)). _Note: You must include at least one of `userId` or `anonymousId` in all identify calls._
`traits` _Object, optional_A dictionary of [traits](/docs/connections/spec/identify#traits) you know about the user. Things like: `email`, `name` or `friends`.
`timestamp` _Date, optional_A JavaScript date object representing when the identify took place. If the identify just happened, leave it out and we'll use the server's time. If you're importing data from the past make sure you to send a `timestamp`.
`context` _Object, optional_A dictionary of extra [context](https://segment.com/docs/connections/spec/common/#context) to attach to the call. _Note: `context` differs from `traits` because it is not attributes of the user itself._
- -Find details on the **identify method payload** in our [Spec](/docs/connections/spec/identify/). - -### Track - -`track` lets you record the actions your users perform. Every action triggers what we call an "event", which can also have associated properties. - -You'll want to track events that are indicators of success for your site, like **Signed Up**, **Item Purchased** or **Article Bookmarked**. - -To get started, we recommend tracking just a few important events. You can always add more later! - -Example anonymous `track` call: - -```javascript -analytics.track({ - anonymousId: '48d213bb-95c3-4f8d-af97-86b2b404dcfe', - event: 'Item Purchased', - properties: { - revenue: 39.95, - shippingMethod: '2-day' - } -}); -``` - -Example identified `track` call: - -```javascript -analytics.track({ - userId: '019mr8mf4r', - event: 'Item Purchased', - properties: { - revenue: 39.95, - shippingMethod: '2-day' - } -}); -``` - -This example `track` call tells us that your user just triggered the **Item Purchased** event with a revenue of $39.95 and chose your hypothetical '2-day' shipping. - -`track` event properties can be anything you want to record. In this case, revenue and shipping method. - -The `track` call has the following fields: - - - - - - - - - - - - - - - - - - - - - - - - - - -
`userId` _String, optional_The ID for this user in your database. _Note: at least one of `userId` or `anonymousId` must be included in any track call._
`anonymousId` _String, optional_An ID associated with the user when you don't know who they are (for example, [the anonymousId generated by `analytics.js`](/docs/connections/sources/catalog/libraries/website/javascript/#anonymous-id)). _Note: You must include at least one of `userId` or `anonymousId` in all track calls._
`event` _String_The name of the event you're tracking. We recommend human-readable names like `Song Played` or `Status Updated`.
`properties` _Object, optional_A dictionary of properties for the event. If the event was `Product Added`, it might have properties like `price` or `product`.
`timestamp` _Date, optional_A JavaScript date object representing when the track took place. If the track just happened, leave it out and we'll use the server's time. If you're importing data from the past make sure you to send a `timestamp`.
`context` _Object, optional_A dictionary of extra [context](https://segment.com/docs/connections/spec/common/#context) to attach to the call. _Note: `context` differs from `traits` because it is not attributes of the user itself._
- -Find details on **best practices in event naming** as well as the **`track` method payload** in our [Spec](/docs/connections/spec/track/). - -### Page - -The [`page`](/docs/connections/spec/page/) method lets you record page views on your website, along with optional extra information about the page being viewed. - -If you're using our client-side set up in combination with the Node.js library, page calls are **already tracked for you** by default. However, if you want to record your own page views manually and aren't using our client-side library, read on! - -Example `page` call: - -```js -analytics.page({ - userId: '019mr8mf4r', - category: 'Docs', - name: 'Node.js Library', - properties: { - url: 'https://segment.com/docs/connections/sources/catalog/librariesnode', - path: '/docs/connections/sources/catalog/librariesnode/', - title: 'Node.js Library - Segment', - referrer: 'https://github.com/segmentio/analytics-node' - } -}); -``` - -The `page` call has the following fields: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
`userId` _String, optional_The ID for this user in your database. _Note: at least one of `userId` or `anonymousId` must be included in any page call._
`anonymousId` _String, optional_An ID associated with the user when you don't know who they are (eg., [the anonymousId generated by `analytics.js`](/docs/connections/sources/catalog/libraries/website/javascript/#anonymous-id)). _Note: at least one of `userId` or `anonymousId` must be included in any page call._
`category` _String, optional_The category of the page. Useful for things like ecommerce where many pages often live under a larger category.
`name` _String, optional_The name of the page, for example **Signup** or **Home**.
`properties` _Object, optional_A dictionary of properties of the page. A few properties specially recognized and automatically translated: `url`, `title`, `referrer` and `path`, but you can add your own too!
`timestamp` _Date, optional_A JavaScript date object representing when the track took place. If the track just happened, leave it out and we'll use the server's time. If you're importing data from the past make sure you to send a `timestamp`.
`context` _Object, optional_A dictionary of extra [context](https://segment.com/docs/connections/spec/common/#context) to attach to the call. _Note: `context` differs from `traits` because it is not attributes of the user itself._
- -Find details on the **`page` payload** in our [Spec](/docs/connections/spec/page/). - -### Group - -`group` lets you associate an [identified user](/docs/connections/sources/catalog/libraries/server/node/#identify) with a group. A group could be a company, organization, account, project or team! It also lets you record custom traits about the group, like industry or number of employees. - -This is useful for tools like [Intercom](/docs/connections/destinations/catalog/intercom/), [Preact](/docs/connections/destinations/catalog/preact/) and [Totango](/docs/connections/destinations/catalog/totango/), as it ties the user to a **group** of other users. - -Example `group` call: - -```javascript -analytics.group({ - userId: '019mr8mf4r', - groupId: '56', - traits: { - name: 'Initech', - description: 'Accounting Software' - } -}); -``` - -The `group` call has the following fields: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
`userId` _String, optional_The ID for this user in your database. _Note: at least one of `userId` or `anonymousId` must be included in any group call._
`anonymousId` _String, optional_An ID associated with the user when you don't know who they are (eg., [the anonymousId generated by `analytics.js`](/docs/connections/sources/catalog/libraries/website/javascript/#anonymous-id)). _Note: at least one of `userId` or `anonymousId` must be included in any group call._
`groupId` _stringThe ID of the group.
`traits` _dict, optional_A dict of traits you know about the group. For a company, they might be things like `name`, `address`, or `phone`. [Learn more about traits](/docs/connections/spec/group/#traits).
`context` _dict, optional_A dict containing any context about the request. To see the full reference of supported keys, check them out in the [context reference](/docs/connections/spec/common/#context)
`timestamp` _datetime, optional_A `datetime` object representing when the group took place. If the group just happened, leave it out and we'll use the server's time. If you're importing data from the past make sure you send `timestamp`.
`integrations` _dict, optional_A dictionary of destinations to enable or disable
- -Find more details about `group`, including the **`group` payload**, in our [Spec](/docs/connections/spec/group/). - -### Alias - -The `alias` call allows you to associate one identity with another. This is an advanced method and should not be widely used, but is required to manage user identities in _some_ destinations. Other destinations do not support the alias call. - -In [Mixpanel](/docs/connections/destinations/catalog/mixpanel/#alias) it's used to associate an anonymous user with an identified user once they sign up. For [Kissmetrics](/docs/connections/destinations/catalog/kissmetrics/#alias), if your user switches IDs, you can use 'alias' to rename the 'userId'. - -Example `alias` call: - -```javascript -analytics.alias({ - previousId: 'old_id', - userId: 'new_id' -}); -``` - -The `alias` call has the following fields: - - - - - - - - - - -
`userId` _String_The ID for this user in your database.
`previousId` _String_The previous ID to alias from.
- -Here's a full example of how Segment might use the `alias` call: - -```javascript -// the anonymous user does actions ... -analytics.track({ userId: 'anonymous_user', event: 'Anonymous Event' }) -// the anonymous user signs up and is aliased -analytics.alias({ previousId: 'anonymous_user', userId: 'identified@example.com' }) -// the identified user is identified -analytics.identify({ userId: 'identified@example.com', traits: { plan: 'Free' } }) -// the identified user does actions ... -analytics.track({ userId: 'identified@example.com', event: 'Identified Action' }) -``` - -For more details about `alias`, including the **`alias` call payload**, check out the Segment [Spec](/docs/connections/spec/alias/). - ---- - - -## Configuration - -The second argument to the `Analytics` constructor is an optional list of settings to configure the module. - -```javascript -const analytics = new Analytics({ - writeKey: '', - host: 'https://api.segment.io', - path: '/v1/batch', - maxRetries: 3, - maxEventsInBatch: 15, - flushInterval: 10000, - // ... and more! - }) -``` - -Setting | Details -------- | -------- -`writeKey` _string_ | The key that corresponds to your Segment.io project -`host` _string_ | The base URL of the API. The default is: "https://api.segment.io" -`path` _string_ | The API path route. The default is: "/v1/batch" -`maxRetries` _number_ | The number of times to retry flushing a batch. The default is: `3` -`maxEventsInBatch` _number_ | The number of messages to enqueue before flushing. The default is: `15` -`flushInterval` _number_ | The number of milliseconds to wait before flushing the queue automatically. The default is: `10000` -`httpRequestTimeout` | The maximum number of milliseconds to wait for an http request. The default is: `10000` - -### Graceful shutdown -Avoid losing events after shutting down your console. Call `.closeAndFlush()` to stop collecting new events and flush all existing events. If a callback on an event call is included, this also waits for all callbacks to be called, and any of their subsequent promises to be resolved. - -```javascript -await analytics.closeAndFlush() -// or -await analytics.closeAndFlush({ timeout: 5000 }) // force resolve after 5000ms -``` - -Here's an example of how to use graceful shutdown: -```javascript -const app = express() -const server = app.listen(3000) - -const onExit = async () => { - await analytics.closeAndFlush() - server.close(() => { - console.log("Gracefully closing server...") - process.exit() - }) -} -['SIGINT', 'SIGTERM'].forEach((code) => process.on(code, onExit)) -``` - -### Collect unflushed events -If you need to preserve all of your events in the instance of a forced timeout, even ones that came in after analytics.closeAndFlush() was called, you can still collect those events by using: - -```javascript -const unflushedEvents = [] - -analytics.on('call_after_close', (event) => unflushedEvents.push(events)) -await analytics.closeAndFlush() - -console.log(unflushedEvents) // all events that came in after closeAndFlush was called -``` - -## Regional configuration -For Business plans with access to [Regional Segment](/docs/guides/regional-segment), you can use the `host` configuration parameter to send data to the desired region: -1. Oregon (Default) — `api.segment.io/v1` -2. Dublin — `events.eu1.segmentapis.com` - -An example of setting the host to the EU endpoint using the Node library is: -```javascript -const analytics = new Analytics({ - ... - host: "https://events.eu1.segmentapis.com" -}); -``` - -## Error handling - -To keep track of errors, subscribe and log all event delivery errors by running: - -```javascript -const analytics = new Analytics({ writeKey: '' }) - -analytics.on('error', (err) => console.error(err)) -``` - - -### Event emitter interface -The event emitter interface allows you to track when certain things happen in the app, such as a track call or an error, and it will call the function you provided with some arguments when that event happens. - -```javascript -analytics.on('error', (err) => console.error(err)) - -analytics.on('identify', (ctx) => console.log(ctx)) - -analytics.on('track', (ctx) => console.log(ctx)) -``` - -## Plugin architecture -When you develop against Analytics 2.0, the plugins you write can augment functionality, enrich data, and control the flow and delivery of events. From modifying event payloads to changing analytics functionality, plugins help to speed up the process of getting things done. - -Though middlewares function the same as plugins, it's best to use plugins as they are easier to implement and are more testable. - -### Plugin categories -Plugins are bound by Analytics 2.0 which handles operations such as observability, retries, and error handling. There are two different categories of plugins: -* **Critical Plugins**: Analytics.js expects this plugin to be loaded before starting event delivery. Failure to load a critical plugin halts event delivery. Use this category sparingly, and only for plugins that are critical to your tracking. -* **Non-critical Plugins**: Analytics.js can start event delivery before this plugin finishes loading. This means your plugin can fail to load independently from all other plugins. For example, every Analytics.js destination is a non-critical plugin. This makes it possible for Analytics.js to continue working if a partner destination fails to load, or if users have ad blockers turned on that are targeting specific destinations. - -> info "" -> Non-critical plugins are only non-critical from a loading standpoint. For example, if the `before` plugin crashes, this can still halt the event delivery pipeline. - -Non-critical plugins run through a timeline that executes in order of insertion based on the entry type. Segment has these five entry types of non-critical plugins: - -| Type | Details | -| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `before` | Executes before event processing begins. These are plugins that run before any other plugins run.

For example, validating events before passing them along to other plugins. A failure here could halt the event pipeline.

See the example of how Analytics.js uses the [Event Validation plugin](https://github.com/segmentio/analytics-next/blob/master/packages/browser/src/plugins/validation/index.ts){:target="_blank"} to verify that every event has the correct shape. | -| `enrichment` | Executes as the first level of event processing. These plugins modify an event.

See the example of how Analytics.js uses the [Page Enrichment plugin](https://github.com/segmentio/analytics-next/blob/master/packages/browser/src/plugins/page-enrichment/index.ts){:target="_blank"} to enrich every event with page information. | -| `destination` | Executes as events begin to pass off to destinations.

This doesn't modify the event outside of the specific destination, and failure doesn't halt the execution. | -| `after` | Executes after all event processing completes. You can use this to perform cleanup operations.

An example of this is the [Segment.io Plugin](https://github.com/segmentio/analytics-next/blob/master/packages/browser/src/plugins/segmentio/index.ts){:target="_blank"} which waits for destinations to succeed or fail so it can send it observability metrics. | -| `utility` | Executes once during the bootstrap, to give you an outlet to make any modifications as to how Analytics.js works internally. This allows you to augment Analytics.js functionality. | - -### Example plugins -Here's an example of a plugin that converts all track event names to lowercase before the event goes through the rest of the pipeline: - -```js -export const lowercase: Plugin = { - name: 'Lowercase events', - type: 'enrichment', - version: '1.0.0', - - isLoaded: () => true, - load: () => Promise.resolve(), - - track: (ctx) => { - ctx.updateEvent('event', ctx.event.event.toLowerCase()) - return ctx - } -} - -const identityStitching = () => { - let user - - const identity = { - // Identifies your plugin in the Plugins stack. - // Access `window.analytics.queue.plugins` to see the full list of plugins - name: 'Identity Stitching', - // Defines where in the event timeline a plugin should run - type: 'enrichment', - version: '0.1.0', - - // Used to signal that a plugin has been property loaded - isLoaded: () => user !== undefined, - - // Applies the plugin code to every `identify` call in Analytics.js - // You can override any of the existing types in the Segment Spec. - async identify(ctx) { - // Request some extra info to enrich your `identify` events from - // an external API. - const req = await fetch( - `https://jsonplaceholder.typicode.com/users/${ctx.event.userId}` - ) - const userReq = await req.json() - - // ctx.updateEvent can be used to update deeply nested properties - // in your events. It's a safe way to change events as it'll - // create any missing objects and properties you may require. - ctx.updateEvent('traits.custom', userReq) - user.traits(userReq) - - // Every plugin must return a `ctx` object, so that the event - // timeline can continue processing. - return ctx - }, - } - - return identity -} - -``` - -You can view Segment's [existing plugins](https://github.com/segmentio/analytics-next/tree/master/src/plugins){:target="_blank"} to see more examples. - -### Register a plugin -Registering plugins enable you to modify your analytics implementation to best fit your needs. You can register a plugin using this: - -```js -// A promise will resolve once the plugins have been successfully loaded into Analytics.js -// You can register multiple plugins at once by using the variable args interface in Analytics.js -await window.analytics.register(pluginA, pluginB, pluginN) -``` - -## Selecting Destinations - -The `alias`, `group`, `identify`, `page` and `track` calls can all be passed an object of `integrations` that lets you turn certain destinations on or off. By default all destinations are enabled. - -Here's an example with the `integrations` object shown: - -```javascript -analytics.track({ - event: 'Membership Upgraded', - userId: '97234974', - integrations: { - 'All': false, - 'Vero': true, - 'Google Analytics': false - } -}) -``` - -In this case, Segment specifies that they want this `track` to only go to Vero. `All: false` says that no destination should be enabled unless otherwise specified. `Vero: true` turns on Vero. - -Destination flags are **case sensitive** and match [the destination's name in the docs](/docs/connections/destinations/) (for example, "AdLearn Open Platform", "awe.sm", "MailChimp"). In some cases, there may be several names for a destination; if that happens you'll see a "Adding (destination name) to the Integrations Object" section in the destination's doc page with a list of valid names. - -**Note:** - -- Available at the business level, filtering track calls can be done right from the Segment UI on your source schema page. We recommend using the UI if possible since it's a much simpler way of managing your filters and can be updated with no code changes on your side. - -- If you are on a grandfathered plan, events sent server-side that are filtered through the Segment dashboard will still count towards your API usage. - -## Historical Import - -You can import historical data by adding the `timestamp` argument to any of your method calls. This can be helpful if you've just switched to Segment. - -Historical imports can only be done into destinations that can accept historical timestamped data. Most analytics tools like Mixpanel, Amplitude, Kissmetrics, etc. can handle that type of data just fine. One common destination that does not accept historical data is Google Analytics since their API cannot accept historical data. - -**Note:** If you're tracking things that are happening right now, leave out the `timestamp` and Segment's servers will timestamp the requests for you. - - -## Batching - -Segment's libraries are built to support high performance environments. That means it is safe to use Segment's Node library on a web server that's serving hundreds of requests per second. - -Every method you call **doesn't** result in a HTTP request, but is queued in memory instead. Messages are then flushed in batch in the background, which allows for much faster operation. - -By default, Segment's library will flush: - - - The very first time it gets a message. - - Every 15 messages (controlled by `settings.maxEventsInBatch`). - - If 10 seconds has passed since the last flush (controlled by `settings.flushInterval`) - -There is a maximum of `500KB` per batch request and `32KB` per call. - -If you don't want to batch messages, you can turn batching off by setting the `maxEventsInBatch` setting to `1`, like so: - -```javascript -const analytics = new Analytics({ - ... - maxEventsInBatch: 1 -});``` - -Batching means that your message might not get sent right away. Every method call takes an optional `callback`, which you can use to know when a particular message is flushed from the queue, like so: - -```javascript -analytics.track({ - userId: '019mr8mf4r', - event: 'Ultimate Played', - }, - (err, ctx) => { - ... - } -) -``` - -You can also flush on demand. For example, at the end of your program, you need to flush to make sure that nothing is left in the queue. To do that, call the `flush` method: - -```javascript -analytics.flush(function(err, batch){ - console.log('Flushed, and now this program can exit!'); -}); -``` - -## Multiple Clients - -Different parts of your application may require different types of batching, or even sending to multiple Segment sources. In that case, you can initialize multiple instances of `Analytics` with different settings: - -```javascript -const marketingAnalytics = new Analytics({ writeKey: 'MARKETING_WRITE_KEY' }); -const appAnalytics = new Analytics({ writeKey: 'APP_WRITE_KEY' }); -``` - - -## Troubleshooting - -{% include content/troubleshooting-intro.md %} -{% include content/troubleshooting-server-debugger.md %} -{% include content/troubleshooting-server-integration.md %} From ce4c6f7eb8c0cb99ec0f14c0883440618692264a Mon Sep 17 00:00:00 2001 From: stayseesong Date: Mon, 9 Jan 2023 10:54:12 -0800 Subject: [PATCH 06/74] edits --- .../catalog/libraries/server/node/classic.md | 44 +++++++++---------- .../catalog/libraries/server/node/index.md | 2 +- .../libraries/server/node/quickstart.md | 23 +++++++--- 3 files changed, 39 insertions(+), 30 deletions(-) diff --git a/src/connections/sources/catalog/libraries/server/node/classic.md b/src/connections/sources/catalog/libraries/server/node/classic.md index bbe4075540..4c63764887 100644 --- a/src/connections/sources/catalog/libraries/server/node/classic.md +++ b/src/connections/sources/catalog/libraries/server/node/classic.md @@ -6,13 +6,13 @@ hidden: true --- > warning "Deprecation of Analytics Node.js Classic" -> On [date], Segment will end support for Analytics Node.js Classic, which includes versions [1.x.x] and older. Upgrade to the new version of Analytics Node.js. See the updated [Analytics Node.js] docs to learn more. +> On April 1, 2023, Segment will end support for Analytics Node.js Classic, which includes versions 6.2.0 and older. Upgrade to the new version of Analytics Node.js. See the updated [Analytics Node.js docs](/docs/connections/sources/catalog/libraries/server/node) to learn more. Segment's Node.js library lets you record analytics data from your node code. The requests hit Segment's servers, and then Segment routes your data to any destinations you have enabled. The [Segment Node.js library is open-source](https://github.com/segmentio/analytics-node) on GitHub. -All of Segment's server-side libraries are built for high-performance, so you can use them in your web server controller code. This library uses an internal queue to make `identify` and `track` calls non-blocking and fast. It also batches messages and flushes asynchronously to our servers. +All of Segment's server-side libraries are built for high-performance, so you can use them in your web server controller code. This library uses an internal queue to make `identify` and `track` calls non-blocking and fast. It also batches messages and flushes asynchronously to Segment's servers. Want to stay updated on releases? Subscribe to the [release feed](https://github.com/segmentio/analytics-node/releases.atom). @@ -24,7 +24,7 @@ Run: npm install --save analytics-node ``` -This will add our Node library module to your `package.json`. The module exposes an `Analytics` constructor, which you need to initialize with your Segment source's **Write Key**, like so: +This will add Segment's Node library module to your `package.json`. The module exposes an `Analytics` constructor, which you need to initialize with your Segment source's **Write Key**, like so: ```javascript var Analytics = require('analytics-node'); @@ -91,18 +91,18 @@ Field | Details `userId` _String, optional_ | The ID for this user in your database. _Note: at least one of `userId` or `anonymousId` must be included in any identify call._ `anonymousId` _String, optional_ | An ID associated with the user when you don't know who they are (for example, [the anonymousId generated by `analytics.js`](/docs/connections/sources/catalog/libraries/website/javascript/#anonymous-id)). _Note: You must include at least one of `userId` or `anonymousId` in all identify calls._ `traits` _Object, optional_ | A dictionary of [traits](/docs/connections/spec/identify#traits) you know about the user. Things like: `email`, `name` or `friends`. -`timestamp` _Date, optional_ | A JavaScript date object representing when the identify took place. If the identify just happened, leave it out and we'll use the server's time. If you're importing data from the past make sure you to send a `timestamp`. +`timestamp` _Date, optional_ | A JavaScript date object representing when the identify call took place. If the identify just happened, leave it out as Segment will use the server's time. If you're importing data from the past make sure you to send a `timestamp`. `context` _Object, optional_ | A dictionary of extra [context](https://segment.com/docs/connections/spec/common/#context) to attach to the call. _Note: `context` differs from `traits` because it is not attributes of the user itself._ Find details on the **identify method payload** in the Segment [Spec](/docs/connections/spec/identify/). ## Track -`track` lets you record the actions your users perform. Every action triggers what we call an "event", which can also have associated properties. +`track` lets you record the actions your users perform. Every action triggers what Segment calls an "event", which can also have associated properties. You'll want to track events that are indicators of success for your site, like **Signed Up**, **Item Purchased** or **Article Bookmarked**. -To get started, we recommend tracking just a few important events. You can always add more later! +To get started, Segment recommends tracking just a few important events. You can always add more later. Example anonymous `track` call: @@ -130,7 +130,7 @@ analytics.track({ }); ``` -This example `track` call tells us that your user just triggered the **Item Purchased** event with a revenue of $39.95 and chose your hypothetical '2-day' shipping. +This example `track` call tells that your user just triggered the **Item Purchased** event with a revenue of $39.95 and chose your hypothetical '2-day' shipping. `track` event properties can be anything you want to record. In this case, revenue and shipping method. @@ -140,9 +140,9 @@ Field | Details ----- | -------- `userId` _String, optional_ | The ID for this user in your database. _Note: at least one of `userId` or `anonymousId` must be included in any track call. `anonymousId` _String, optional_ | An ID associated with the user when you don't know who they are (for example, [the anonymousId generated by `analytics.js`](/docs/connections/sources/catalog/libraries/website/javascript/#anonymous-id)). _Note: You must include at least one of `userId` or `anonymousId` in all track calls._ -`event` _String_ | The name of the event you're tracking. We recommend human-readable names like `Song Played` or `Status Updated`. +`event` _String_ | The name of the event you're tracking. Segment recommends you use human-readable names like `Song Played` or `Status Updated`. `properties` _Object, optional_ | A dictionary of properties for the event. If the event was `Product Added`, it might have properties like `price` or `product`. -`timestamp` _Date, optional_ | A JavaScript date object representing when the track took place. If the track just happened, leave it out and we'll use the server's time. If you're importing data from the past make sure you to send a `timestamp`. +`timestamp` _Date, optional_ | A JavaScript date object representing when the track took place. If the track just happened, leave it out as Segment will use the server's time. If you're importing data from the past make sure you to send a `timestamp`. `context` _Object, optional_ | A dictionary of extra [context](https://segment.com/docs/connections/spec/common/#context) to attach to the call. _Note: `context` differs from `traits` because it is not attributes of the user itself._ Find details on **best practices in event naming** as well as the **`track` method payload** in the Segment [Spec](/docs/connections/spec/track/). @@ -151,7 +151,7 @@ Find details on **best practices in event naming** as well as the **`track` meth The [`page`](/docs/connections/spec/page/) method lets you record page views on your website, along with optional extra information about the page being viewed. -If you're using our client-side set up in combination with the Node.js library, page calls are **already tracked for you** by default. However, if you want to record your own page views manually and aren't using our client-side library, read on! +If you're using Segment's client-side set up in combination with the Node.js library, page calls are already tracked for you by default. Example `page` call: @@ -174,11 +174,11 @@ The `page` call has the following fields: Field | Details ----- | -------- `userId` _String, optional_ | The ID for this user in your database. _Note: at least one of `userId` or `anonymousId` must be included in any page call. -`anonymousId` _String, optional_ | An ID associated with the user when you don't know who they are (eg., [the anonymousId generated by `analytics.js`](/docs/connections/sources/catalog/libraries/website/javascript/#anonymous-id)). _Note: at least one of `userId` or `anonymousId` must be included in any page call._ +`anonymousId` _String, optional_ | An ID associated with the user when you don't know who they are (for example, [the anonymousId generated by `analytics.js`](/docs/connections/sources/catalog/libraries/website/javascript/#anonymous-id)). _Note: at least one of `userId` or `anonymousId` must be included in any page call._ `category` _String, optional_ | The category of the page. Useful for things like ecommerce where many pages often live under a larger category. `name` _String, optional_ | The name of the page, for example **Signup** or **Home**. `properties` _Object, optional_ | A dictionary of properties of the page. A few properties specially recognized and automatically translated: `url`, `title`, `referrer` and `path`, but you can add your own too. -`timestamp` _Date, optional_ | A JavaScript date object representing when the track took place. If the track just happened, leave it out and we'll use the server's time. If you're importing data from the past make sure you to send a `timestamp`. +`timestamp` _Date, optional_ | A JavaScript date object representing when the track took place. If the track just happened, leave it out as Segment will use the server's time. If you're importing data from the past make sure you to send a `timestamp`. `context` _Object, optional_ | A dictionary of extra [context](https://segment.com/docs/connections/spec/common/#context) to attach to the call. _Note: `context` differs from `traits` because it is not attributes of the user itself._ Find details on the **`page` payload** in the Segment [Spec](/docs/connections/spec/page/). @@ -207,11 +207,11 @@ The `group` call has the following fields: Field | Details ----- | -------- `userId` _String, optional_ | The ID for this user in your database. _Note: at least one of `userId` or `anonymousId` must be included in any group call. -`anonymousId` _String, optional_ | An ID associated with the user when you don't know who they are (eg., [the anonymousId generated by `analytics.js`](/docs/connections/sources/catalog/libraries/website/javascript/#anonymous-id)). _Note: at least one of `userId` or `anonymousId` must be included in any group call._ +`anonymousId` _String, optional_ | An ID associated with the user when you don't know who they are (for example, [the anonymousId generated by `analytics.js`](/docs/connections/sources/catalog/libraries/website/javascript/#anonymous-id)). _Note: at least one of `userId` or `anonymousId` must be included in any group call._ `groupId` _string | The ID of the group. `traits` _dict, optional_ | A dict of traits you know about the group. For a company, they might be things like `name`, `address`, or `phone`. [Learn more about traits](/docs/connections/spec/group/#traits). `context` _dict, optional_ | A dict containing any context about the request. To see the full reference of supported keys, check them out in the [context reference](/docs/connections/spec/common/#context) -`timestamp` _datetime, optional_ | A `datetime` object representing when the group took place. If the group just happened, leave it out and we'll use the server's time. If you're importing data from the past make sure you send `timestamp`. +`timestamp` _datetime, optional_ | A `datetime` object representing when the group took place. If the group just happened, leave it out as Segment uses the server's time. If you're importing data from the past make sure you send `timestamp`. `integrations` _dict, optional_ | A dictionary of destinations to enable or disable. Find more details about `group`, including the **`group` payload**, in the Segment [Spec](/docs/connections/spec/group/). @@ -238,7 +238,7 @@ Field | Details `userId` _String_ | The ID for this user in your database. `previousId` _String_ | The previous ID to alias from. -Here's a full example of how we might use the `alias` call: +Here's a full example of how Segment might use the `alias` call: ```javascript // the anonymous user does actions ... @@ -251,7 +251,7 @@ analytics.identify({ userId: 'identified@example.com', traits: { plan: 'Free' } analytics.track({ userId: 'identified@example.com', event: 'Identified Action' }) ``` -For more details about `alias`, including the **`alias` call payload**, check out our [Spec](/docs/connections/spec/alias/). +For more details about `alias`, including the **`alias` call payload**, check out the [Spec](/docs/connections/spec/alias/). --- @@ -327,13 +327,13 @@ analytics.track({ }) ``` -In this case, we're specifying that we want this `track` to only go to Vero. `All: false` says that no destination should be enabled unless otherwise specified. `Vero: true` turns on Vero, etc. +In this case, Segment specifies the `track` to only go to Vero. `All: false` says that no destination should be enabled unless otherwise specified. `Vero: true` turns on Vero, etc. -Destination flags are **case sensitive** and match [the destination's name in the docs](/docs/connections/destinations/) (i.e. "AdLearn Open Platform", "awe.sm", "MailChimp", etc.). In some cases, there may be several names for a destination; if that happens you'll see a "Adding (destination name) to the Integrations Object" section in the destination's doc page with a list of valid names. +Destination flags are **case sensitive** and match [the destination's name in the docs](/docs/connections/destinations/) (for example, "AdLearn Open Platform", "awe.sm", "MailChimp"). In some cases, there may be several names for a destination; if that happens you'll see a "Adding (destination name) to the Integrations Object" section in the destination's doc page with a list of valid names. **Note:** -- Available at the business level, filtering track calls can be done right from the Segment UI on your source schema page. We recommend using the UI if possible since it's a much simpler way of managing your filters and can be updated with no code changes on your side. +- Available at the business level, filtering track calls can be done right from the Segment UI on your source schema page. Segment recommends using the UI if possible since it's a much simpler way of managing your filters and can be updated with no code changes on your side. - If you are on a grandfathered plan, events sent server-side that are filtered through the Segment dashboard will still count towards your API usage. @@ -343,16 +343,16 @@ You can import historical data by adding the `timestamp` argument to any of your Historical imports can only be done into destinations that can accept historical timestamped data. Most analytics tools like Mixpanel, Amplitude, Kissmetrics, etc. can handle that type of data just fine. One common destination that does not accept historical data is Google Analytics since their API cannot accept historical data. -**Note:** If you're tracking things that are happening right now, leave out the `timestamp` and our servers will timestamp the requests for you. +**Note:** If you're tracking things that are happening right now, leave out the `timestamp` and Segment's servers will timestamp the requests for you. ## Batching -Our libraries are built to support high performance environments. That means it is safe to use our Node library on a web server that's serving hundreds of requests per second. +Segment's libraries are built to support high performance environments. That means it is safe to use the Segment Node library on a web server that's serving hundreds of requests per second. Every method you call **does not** result in an HTTP request, but is queued in memory instead. Messages are then flushed in batch in the background, which allows for much faster operation. -By default, our library will flush: +By default, Segment's library flushes: - The very first time it gets a message. - Every 20 messages (controlled by `options.flushAt`). diff --git a/src/connections/sources/catalog/libraries/server/node/index.md b/src/connections/sources/catalog/libraries/server/node/index.md index ff4141c812..cb78f333f5 100644 --- a/src/connections/sources/catalog/libraries/server/node/index.md +++ b/src/connections/sources/catalog/libraries/server/node/index.md @@ -13,7 +13,7 @@ All of Segment's server-side libraries are built for high-performance, so you ca > info "Using Analytics for Node.js Classic?" > If you’re still using the classic version of Analytics for Node.js, you can refer to the documentation [here](/docs/connections/sources/catalog/libraries/server/node/classic). ->

On [date], Segment will end support for Analytics Node.js Classic, which includes versions [1.x.x] and older. Upgrade to Analytics Node.js 2.0. See the Analytics Node.js 2.0 docs to learn more. +>

On April 1, 2023, Segment will end support for Analytics Node.js Classic, which includes versions 6.2.0 and older. Upgrade to new Analytics Node.js. See the updated [Analytics Node.js quickstart guide](/docs/connections/sources/catalog/libraries/server/node/quickstart/) to learn more. ## Getting Started diff --git a/src/connections/sources/catalog/libraries/server/node/quickstart.md b/src/connections/sources/catalog/libraries/server/node/quickstart.md index 6064c5a537..a2aafff37b 100644 --- a/src/connections/sources/catalog/libraries/server/node/quickstart.md +++ b/src/connections/sources/catalog/libraries/server/node/quickstart.md @@ -1,11 +1,11 @@ --- -title: 'Quickstart: Node.js Classic' +title: 'Quickstart: Node.js' redirect_from: '/connections/sources/catalog/libraries/server/node-js/quickstart/' strat: node-js --- > warning "Deprecation of Analytics Node.js Classic" -> On [date], Segment will end support for Analytics Node.js Classic, which includes versions [1.x.x] and older. Upgrade to Analytics Node.js 2.0. See the [Analytics Node.js 2.0] docs to learn more. +> On [date], Segment will end support for [Analytics Node.js Classic](/docs/connections/sources/catalog/libraries/server/node/classic/), which includes versions [6.2.0] and older. Upgrade to Analytics Node.js 2.0. See the [Analytics Node.js 2.0] docs to learn more. This tutorial will help you start sending data from your Node servers to Segment and any destination, using Segment's Node library. Check out the full documentation for [Analytics Node.js](/docs/connections/sources/catalog/libraries/server/node) to learn more. @@ -17,19 +17,28 @@ To get started with Analytics Node.js: 4. Give the source a display name, and enter the URL the source will collect data from. * When you create a Source in the Segment web app, it tells the Segment servers that you'll be sending data from a specific source type. When you create or change a Source in the Segment app, Segment generates a new Write Key for that source. You use the write key in your code to tell the Segment servers where the data is coming from, so Segment can route it to your destinations and other tools. 2. Install the module. - 1. Run the following npm command to install Segment: + 1. Run the following commands to install Segment: ``` - npm install --save analytics-node + # npm + npm install @segment/analytics-node + # yarn + yarn add @segment/analytics-node + # pnpm + pnpm install @segment/analytics-node ``` This will add the Node library module to your `package.json`. The module exposes an `Analytics` constructor, which you need to initialize with your Segment project's **Write Key**, like so: ```javascript - var Analytics = require('analytics-node'); - var analytics = new Analytics('YOUR_WRITE_KEY'); + import { Analytics } from '@segment/analytics-node' + // or, if you use require: + const { Analytics } = require('@segment/analytics-node') + + // instantiation + const analytics = new Analytics({ writeKey: '' }) ``` - This will create an instance of `Analytics` that you can use to send data to Segment for your project. The default initialization settings are production-ready and queue 20 messages before sending any requests. In development you might want to use [development settings](/docs/connections/sources/catalog/libraries/server/node#development). + This creates an instance of `Analytics` that you can use to send data to Segment for your project. The default initialization settings are production-ready and queue 20 messages before sending any requests. In development you might want to use [development settings](/docs/connections/sources/catalog/libraries/server/node#development). 3. Identify Users. * **Note:** For any of the different methods described in this quickstart, you can replace the properties and traits in the code samples with variables that represent the data collected. From 5b7df8d6c98ee639680902869f85ab3d6d34501c Mon Sep 17 00:00:00 2001 From: stayseesong Date: Mon, 9 Jan 2023 11:20:00 -0800 Subject: [PATCH 07/74] [netlify-build] --- .../sources/catalog/libraries/server/node/index.md | 8 ++++---- .../sources/catalog/libraries/server/node/quickstart.md | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/connections/sources/catalog/libraries/server/node/index.md b/src/connections/sources/catalog/libraries/server/node/index.md index cb78f333f5..85d65f2f17 100644 --- a/src/connections/sources/catalog/libraries/server/node/index.md +++ b/src/connections/sources/catalog/libraries/server/node/index.md @@ -1,11 +1,11 @@ --- title: Analytics for Node.js redirect_from: '/connections/sources/catalog/libraries/server/node-js/' -repo: analytics-node +repo: analytics-next strat: node-js --- -Segment's Analytics Node.js 2.0 library lets you record analytics data from your node code. The requests hit Segment's servers, and then Segment routes your data to any destinations you have enabled. +Segment's Analytics Node.js library lets you record analytics data from your node code. The requests hit Segment's servers, and then Segment routes your data to any destinations you have enabled. The [Segment Analytics Node.js Next library is open-source](https://github.com/segmentio/analytics-next/tree/master/packages/node){:target="_blank"} on GitHub. @@ -42,7 +42,7 @@ All of Segment's server-side libraries are built for high-performance, so you ca const analytics = new Analytics({ writeKey: '' }) ``` - Be sure to replace `YOUR_WRITE_KEY` with your actual **Write Key** which you can find in Segment under your source settings. + Be sure to replace `YOUR_WRITE_KEY` with your actual **Write Key** which you can find in Segment by navigating to: **Connections > Sources** and selecting your source and going to the **Settings** tab. This creates an instance of `Analytics` that you can use to send data to Segment for your project. The default initialization settings are production-ready and queue 20 messages before sending any requests. In development you might want to use [development settings](/docs/connections/sources/catalog/libraries/server/node/#development). @@ -288,7 +288,7 @@ Setting | Details `flushInterval` _number_ | The number of milliseconds to wait before flushing the queue automatically. The default is: `10000` `httpRequestTimeout` | The maximum number of milliseconds to wait for an http request. The default is: `10000` -### Graceful shutdown +## Graceful shutdown Avoid losing events after shutting down your console. Call `.closeAndFlush()` to stop collecting new events and flush all existing events. If a callback on an event call is included, this also waits for all callbacks to be called, and any of their subsequent promises to be resolved. ```javascript diff --git a/src/connections/sources/catalog/libraries/server/node/quickstart.md b/src/connections/sources/catalog/libraries/server/node/quickstart.md index a2aafff37b..4f6f074da3 100644 --- a/src/connections/sources/catalog/libraries/server/node/quickstart.md +++ b/src/connections/sources/catalog/libraries/server/node/quickstart.md @@ -5,7 +5,7 @@ strat: node-js --- > warning "Deprecation of Analytics Node.js Classic" -> On [date], Segment will end support for [Analytics Node.js Classic](/docs/connections/sources/catalog/libraries/server/node/classic/), which includes versions [6.2.0] and older. Upgrade to Analytics Node.js 2.0. See the [Analytics Node.js 2.0] docs to learn more. +> On April 1, 2023, Segment will end support for [Analytics Node.js Classic](/docs/connections/sources/catalog/libraries/server/node/classic/), which includes versions 6.2.0 and older. Upgrade to new Analytics Node.js. See the [updated Analytics Node.js docs](/docs/connections/sources/catalog/libraries/server/node/) to learn more. This tutorial will help you start sending data from your Node servers to Segment and any destination, using Segment's Node library. Check out the full documentation for [Analytics Node.js](/docs/connections/sources/catalog/libraries/server/node) to learn more. From 99227523699968645632741f16c05c7718f7a74b Mon Sep 17 00:00:00 2001 From: "Dery J. Aldeano" <93936421+daldeano-segment@users.noreply.github.com> Date: Mon, 9 Jan 2023 15:59:19 -0500 Subject: [PATCH 08/74] What to do when credentials have updated/changed --- .../sources/catalog/cloud-apps/facebook-lead-ads/index.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/connections/sources/catalog/cloud-apps/facebook-lead-ads/index.md b/src/connections/sources/catalog/cloud-apps/facebook-lead-ads/index.md index 05a25c7fe0..aa3b40734a 100644 --- a/src/connections/sources/catalog/cloud-apps/facebook-lead-ads/index.md +++ b/src/connections/sources/catalog/cloud-apps/facebook-lead-ads/index.md @@ -24,6 +24,10 @@ Segment lets you make the most of your leads by automatically sending them to yo 6. Verify that your leads are now flowing into Segment. You can quickly test this connection by sending a test lead to Segment with Facebook's [Lead Ad testing tool](https://developers.facebook.com/tools/lead-ads-testing). 7. Click on the Debugger to see a live stream of data coming in from Facebook. You should see your leads come in as Identify calls. +**Note:** It's important to note that when a connected account changes or updates their credentials, a new Facebook Lead Ads source will have to be created instead of solely reauthorizing the account as this is how FB has decided to operate. Reauthorization implies that a new token gets generated, but the token is not the sole link between Segment and Facebook. A Segment app is installed onto an FB Account and that will be the sprouting point from where tokens are generated. + +The reason we can't disconnect and reconnect the CRM on Facebook with reauthorization is because the creation of a new token will mean to invalidate the rest of the tokens attached to the same Facebook account. For example, let's imagine you have a Facebook Account with three accounts: Account A, Account B, and Account C. You connect Segment to all of them and then generate tokens respectively. Later on, you decide for whatever reason to reauthorize the "Account A" page, the other tokens will be invalidated, and the pages will stop working. + ### Permissions **Setup** Admin permissions are required for setup. Advertiser or lower permissions will prevent the source from connecting. From 3adaa58d5b50463a5749917a4c1dd510cce19f5f Mon Sep 17 00:00:00 2001 From: Atif Javed <46914900+muhammadatifjav@users.noreply.github.com> Date: Wed, 11 Jan 2023 11:09:36 +1100 Subject: [PATCH 09/74] add details about data type mismatch --- src/connections/storage/warehouses/schema.md | 24 ++++++++------------ 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/connections/storage/warehouses/schema.md b/src/connections/storage/warehouses/schema.md index 6e64abf66b..9dbe1293fa 100644 --- a/src/connections/storage/warehouses/schema.md +++ b/src/connections/storage/warehouses/schema.md @@ -401,32 +401,28 @@ ORDER BY day | 2014-07-20 | $1,595 | | 2014-07-21 | $2,350 | +## Schema Evolution and Compatibility + ### New Columns New event properties and traits create columns. Segment processes the incoming data in batches, based on either data size or an interval of time. If the table doesn't exist we lock and create the table. If the table exists but new columns need to be created, we perform a diff and alter the table to append new columns. When Segment process a new batch and discover a new column to add, we take the most recent occurrence of a column and choose its datatype. +### Data Types -### Supported Data Types -Data types are set up in your warehouse based on the first value that comes in from a source. For example, if the first value that came in from a source was a string, Segment would set the data type in the warehouse to `string`. - -The data types that Segment currently supports include: - -#### `timestamp` - -#### `integer` +The data types that Segment currently supports include `timestamp`, `integer`, `float`, `boolean`, and `varchar`. -#### `float` +Data types are set up in your warehouse based on the first value that comes in from a source. For example, if the first value that came in from a source was a string, Segment would set the data type in the warehouse to `string`. -#### `boolean` +In cases where a data type is determined incorrectly, the support team can help you update the data type. As an example, if a field can include float values as well as integers, but the first value we received was an integer, we will set the data type of the field to integer, resulting in a loss of precision. -#### `varchar` +To update the data type, the support team will update the internal schema that Segment uses to infer your warehouse schema. We will start syncing the data with the correct data type after the change is made. However, if you want to backfill all historical data correctly, it will be required to drop the impacted tables on your end so Segment can recreate them in the correct datatype, and then backfill those tables. -> note " " -> To change data types after they've been determined, please reach out to [Segment Support](https://segment.com/help/contact) for assistance. +To request data types changes, please reach out to [Segment Support](https://segment.com/help/contact) for assistance, and provide with these details for the affected columns in the following format: +`....` -## Column Sizing +### Column Sizing After analyzing the data from dozens of customers, we set the string column length limit at 512 characters. Longer strings are truncated. We found this was the sweet spot for good performance and ignoring non-useful data. From dad8619de3da032edd89646690cf21737cfba1fb Mon Sep 17 00:00:00 2001 From: Atif Javed <46914900+muhammadatifjav@users.noreply.github.com> Date: Wed, 11 Jan 2023 11:39:36 +1100 Subject: [PATCH 10/74] update faq with data type --- src/connections/storage/warehouses/faq.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/connections/storage/warehouses/faq.md b/src/connections/storage/warehouses/faq.md index bae26faa32..1c54913ff4 100644 --- a/src/connections/storage/warehouses/faq.md +++ b/src/connections/storage/warehouses/faq.md @@ -74,7 +74,7 @@ Your source slug can be found in the URL when you're looking at the source desti Your warehouse id appears in the URL when you look at the [warehouse destinations page](https://app.segment.com/goto-my-workspace/warehouses/). The URL structure looks like this: -​​`app.segment.com/[my-workspace]/warehouses/[my-warehouse-id]/overview` +`app.segment.com/[my-workspace]/warehouses/[my-warehouse-id]/overview` ## How fresh is the data in Segment Warehouses? @@ -170,3 +170,7 @@ To change the name of your schema without disruptions: 11. Select the warehouse you disabled syncs for from the list of destinations. 3. On the overview page for your source, select **Settings**. 4. Enable the **Sync Data** toggle and click **Save Settings**. + +## Can I change the data type of a column in the warehouse? + +Yes, data types are set up in your warehouse based on the first value that comes in from a source. However, you can request the support team to update the data type. To learn more on how to request, check out [Data Types](/docs/connections/storage/warehouses/schema/#schema-evolution-and-compatibility) section. From 2c43b179f08998a33c2287b48bd2d511a6b68d77 Mon Sep 17 00:00:00 2001 From: Ashton Huxtable <78318468+ashton-huxtable@users.noreply.github.com> Date: Wed, 11 Jan 2023 09:09:35 -0700 Subject: [PATCH 11/74] Update faq.md --- src/protocols/faq.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/protocols/faq.md b/src/protocols/faq.md index 4414d62e22..ae0813f900 100644 --- a/src/protocols/faq.md +++ b/src/protocols/faq.md @@ -13,13 +13,15 @@ You can subscribe to a variety of Protocols specific alerts through the workspac ### How can I get notified when someone makes a change to my tracking plan? -You can forward notifications from Protocols to a new Segment Source, which can then send them to notification tools such as Slack webhook. +You can forward notifications from Protocols to a new Segment Source, which can then send them to notification tools such as Slack webhook. You can also forward these Protocols alerts to any (cloud-mode) Segment destination that accepts Track calls, including data warehouses. Most customers record these activity feed events to a data warehouse for analysis. ### How do I get notified when new violations are generated? Can I create custom violation notifications? -You can enable [violation event forwarding](/docs/protocols/validate/forward-violations/) to start delivering violations as Track calls to a Segment Source. From there, you can forward the events to any Segment destination that accepts Track calls. +You can enable [violation event forwarding](/docs/protocols/validate/forward-violations/) to start delivering violations as Track calls to a Segment Source. From there, you can forward the events to any Segment destination that accepts Track calls. + +You can also utilize the Slack Actions destination here. The Actions destination will allow you to set event triggers for context fields, meaning events with Violations can be sent directly from the source. ## Protocols Tracking Plan From 9cd69f6209c766357853f8cf36808e9a2c50313c Mon Sep 17 00:00:00 2001 From: Olesia Date: Wed, 11 Jan 2023 12:20:33 -0500 Subject: [PATCH 12/74] Add information about deleting/changing ExternalID's --- src/profiles/faqs.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/profiles/faqs.md b/src/profiles/faqs.md index 4246a3e968..808b6e9e0b 100644 --- a/src/profiles/faqs.md +++ b/src/profiles/faqs.md @@ -47,3 +47,6 @@ If two merged user profiles contain conflicting profile attributes, Segment sele ## What identifiers can the merged profile be queried/updated with? Any of the external IDs can be used to query a profile. When a profile is requested, Segment traverses the merge graph and resolves all merged profiles. The result is a single profile, with the latest state of all traits, events, and identifiers. + +### Can ExternalID's be changed or removed from the profiles? +No. Because ExternalID's are used by the Identity Graph, they remain for the lifetime of the user profile. From b5d18b518af69f7cb6e48e7b215cca3b036811b6 Mon Sep 17 00:00:00 2001 From: stayseesong Date: Wed, 11 Jan 2023 14:55:42 -0800 Subject: [PATCH 13/74] edits [netlify-build] --- .../sources/catalog/libraries/server/node/classic.md | 4 ++-- .../sources/catalog/libraries/server/node/index.md | 11 +++++------ .../catalog/libraries/server/node/quickstart.md | 5 +++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/connections/sources/catalog/libraries/server/node/classic.md b/src/connections/sources/catalog/libraries/server/node/classic.md index 4c63764887..13a4f57c1f 100644 --- a/src/connections/sources/catalog/libraries/server/node/classic.md +++ b/src/connections/sources/catalog/libraries/server/node/classic.md @@ -5,8 +5,8 @@ strat: node-js hidden: true --- -> warning "Deprecation of Analytics Node.js Classic" -> On April 1, 2023, Segment will end support for Analytics Node.js Classic, which includes versions 6.2.0 and older. Upgrade to the new version of Analytics Node.js. See the updated [Analytics Node.js docs](/docs/connections/sources/catalog/libraries/server/node) to learn more. +> info "Upgrade to the new version of Analytics Node.js" +> Upgrade to the new version of Analytics Node.js. See the updated [Analytics Node.js docs](/docs/connections/sources/catalog/libraries/server/node) to learn more. Segment's Node.js library lets you record analytics data from your node code. The requests hit Segment's servers, and then Segment routes your data to any destinations you have enabled. diff --git a/src/connections/sources/catalog/libraries/server/node/index.md b/src/connections/sources/catalog/libraries/server/node/index.md index 85d65f2f17..523b89d0dc 100644 --- a/src/connections/sources/catalog/libraries/server/node/index.md +++ b/src/connections/sources/catalog/libraries/server/node/index.md @@ -5,16 +5,15 @@ repo: analytics-next strat: node-js --- +> info "" +> This version of Analytics for Node.js is in beta and Segment is actively working on this feature. Segment's [First-Access and Beta terms](https://segment.com/legal/first-access-beta-preview/) govern this feature. If you’re using the classic version of Analytics for Node.js, you can refer to the documentation [here](/docs/connections/sources/catalog/libraries/server/node/classic). + Segment's Analytics Node.js library lets you record analytics data from your node code. The requests hit Segment's servers, and then Segment routes your data to any destinations you have enabled. The [Segment Analytics Node.js Next library is open-source](https://github.com/segmentio/analytics-next/tree/master/packages/node){:target="_blank"} on GitHub. All of Segment's server-side libraries are built for high-performance, so you can use them in your web server controller code. This library uses an internal queue to make `identify` and `track` calls non-blocking and fast. It also batches messages and flushes asynchronously to Segment's servers. -> info "Using Analytics for Node.js Classic?" -> If you’re still using the classic version of Analytics for Node.js, you can refer to the documentation [here](/docs/connections/sources/catalog/libraries/server/node/classic). ->

On April 1, 2023, Segment will end support for Analytics Node.js Classic, which includes versions 6.2.0 and older. Upgrade to new Analytics Node.js. See the updated [Analytics Node.js quickstart guide](/docs/connections/sources/catalog/libraries/server/node/quickstart/) to learn more. - ## Getting Started > warning "" @@ -504,7 +503,6 @@ Every method you call **doesn't** result in a HTTP request, but is queued in mem By default, Segment's library will flush: - - The very first time it gets a message. - Every 15 messages (controlled by `settings.maxEventsInBatch`). - If 10 seconds has passed since the last flush (controlled by `settings.flushInterval`) @@ -516,7 +514,8 @@ If you don't want to batch messages, you can turn batching off by setting the `m const analytics = new Analytics({ ... maxEventsInBatch: 1 -});``` +}); +``` Batching means that your message might not get sent right away. Every method call takes an optional `callback`, which you can use to know when a particular message is flushed from the queue, like so: diff --git a/src/connections/sources/catalog/libraries/server/node/quickstart.md b/src/connections/sources/catalog/libraries/server/node/quickstart.md index 4f6f074da3..8d9fb8ef7e 100644 --- a/src/connections/sources/catalog/libraries/server/node/quickstart.md +++ b/src/connections/sources/catalog/libraries/server/node/quickstart.md @@ -4,8 +4,9 @@ redirect_from: '/connections/sources/catalog/libraries/server/node-js/quickstart strat: node-js --- -> warning "Deprecation of Analytics Node.js Classic" -> On April 1, 2023, Segment will end support for [Analytics Node.js Classic](/docs/connections/sources/catalog/libraries/server/node/classic/), which includes versions 6.2.0 and older. Upgrade to new Analytics Node.js. See the [updated Analytics Node.js docs](/docs/connections/sources/catalog/libraries/server/node/) to learn more. +> info "" +> This version of Analytics for Node.js is in beta and Segment is actively working on this feature. Segment's [First-Access and Beta terms](https://segment.com/legal/first-access-beta-preview/) govern this feature. +>

If you’re using the [classic version of Analytics for Node.js](/docs/connections/sources/catalog/libraries/server/node/classic/), consider upgrading to the [latest Analytics for Node.js library](/docs/connections/sources/catalog/libraries/server/node). This tutorial will help you start sending data from your Node servers to Segment and any destination, using Segment's Node library. Check out the full documentation for [Analytics Node.js](/docs/connections/sources/catalog/libraries/server/node) to learn more. From 7ef9da797e2f13948c87e9737f0093c7f20428ef Mon Sep 17 00:00:00 2001 From: Atif Javed <46914900+muhammadatifjav@users.noreply.github.com> Date: Thu, 12 Jan 2023 15:48:14 +1100 Subject: [PATCH 14/74] updated faq --- src/connections/storage/warehouses/faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/connections/storage/warehouses/faq.md b/src/connections/storage/warehouses/faq.md index 1c54913ff4..27086d84a1 100644 --- a/src/connections/storage/warehouses/faq.md +++ b/src/connections/storage/warehouses/faq.md @@ -173,4 +173,4 @@ To change the name of your schema without disruptions: ## Can I change the data type of a column in the warehouse? -Yes, data types are set up in your warehouse based on the first value that comes in from a source. However, you can request the support team to update the data type. To learn more on how to request, check out [Data Types](/docs/connections/storage/warehouses/schema/#schema-evolution-and-compatibility) section. +Yes, data types are set up in your warehouse based on the first value that comes in from a source. However, you can request the support team to update the data type by reaching out to [support](https://app.segment.com/workspaces?contact=1). To learn more, check out [Data Types](/docs/connections/storage/warehouses/schema/#schema-evolution-and-compatibility) section. From 3a651f9b5341ac0273700cf93d98f960a2374824 Mon Sep 17 00:00:00 2001 From: Atif Javed <46914900+muhammadatifjav@users.noreply.github.com> Date: Thu, 12 Jan 2023 15:53:35 +1100 Subject: [PATCH 15/74] reverted data types to list --- src/connections/storage/warehouses/schema.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/connections/storage/warehouses/schema.md b/src/connections/storage/warehouses/schema.md index 9dbe1293fa..90bf68cd1e 100644 --- a/src/connections/storage/warehouses/schema.md +++ b/src/connections/storage/warehouses/schema.md @@ -411,7 +411,17 @@ When Segment process a new batch and discover a new column to add, we take the m ### Data Types -The data types that Segment currently supports include `timestamp`, `integer`, `float`, `boolean`, and `varchar`. +The data types that Segment currently supports include: + +#### `timestamp` + +#### `integer` + +#### `float` + +#### `boolean` + +#### `varchar` Data types are set up in your warehouse based on the first value that comes in from a source. For example, if the first value that came in from a source was a string, Segment would set the data type in the warehouse to `string`. From d251556ec445b0f4e814ab2944044911014fc8e0 Mon Sep 17 00:00:00 2001 From: "Dery J. Aldeano" <93936421+daldeano-segment@users.noreply.github.com> Date: Thu, 12 Jan 2023 13:36:50 -0500 Subject: [PATCH 16/74] Added troubleshooting section, edited explanatio --- .../catalog/cloud-apps/facebook-lead-ads/index.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/connections/sources/catalog/cloud-apps/facebook-lead-ads/index.md b/src/connections/sources/catalog/cloud-apps/facebook-lead-ads/index.md index aa3b40734a..80f054d18f 100644 --- a/src/connections/sources/catalog/cloud-apps/facebook-lead-ads/index.md +++ b/src/connections/sources/catalog/cloud-apps/facebook-lead-ads/index.md @@ -24,10 +24,6 @@ Segment lets you make the most of your leads by automatically sending them to yo 6. Verify that your leads are now flowing into Segment. You can quickly test this connection by sending a test lead to Segment with Facebook's [Lead Ad testing tool](https://developers.facebook.com/tools/lead-ads-testing). 7. Click on the Debugger to see a live stream of data coming in from Facebook. You should see your leads come in as Identify calls. -**Note:** It's important to note that when a connected account changes or updates their credentials, a new Facebook Lead Ads source will have to be created instead of solely reauthorizing the account as this is how FB has decided to operate. Reauthorization implies that a new token gets generated, but the token is not the sole link between Segment and Facebook. A Segment app is installed onto an FB Account and that will be the sprouting point from where tokens are generated. - -The reason we can't disconnect and reconnect the CRM on Facebook with reauthorization is because the creation of a new token will mean to invalidate the rest of the tokens attached to the same Facebook account. For example, let's imagine you have a Facebook Account with three accounts: Account A, Account B, and Account C. You connect Segment to all of them and then generate tokens respectively. Later on, you decide for whatever reason to reauthorize the "Account A" page, the other tokens will be invalidated, and the pages will stop working. - ### Permissions **Setup** Admin permissions are required for setup. Advertiser or lower permissions will prevent the source from connecting. @@ -137,3 +133,12 @@ Destinations that can be used with the Facebook Lead Ads Source: - Madkudu Log into your downstream tools and check that your events are populating, and contain all of the properties you expect. If all your events and properties are not showing up, refer to the [Destination docs](https://segment.com/docs/connections/destinations/) for troubleshooting. + + +## Troubleshooting + +### No Data After Updating Credentials + +When a connected account changes or updates their credentials, the customer will have to create a new Facebook Lead Ads source. Reauthorization implies that a new token gets generated, but the token is not the sole link between Segment and Facebook. A Segment app is installed onto an FB Account and that will be the sprouting point from where tokens are generated. The reason we can't disconnect and reconnect the CRM on Facebook with reauthorization is because the creation of a new token will mean to invalidate the rest of the tokens attached to the same Facebook account. + +For example, let's imagine you have a Facebook Account with three accounts: Account A, Account B, and Account C. You connect Segment to all of them and then generate tokens respectively. Later on, you decide for whatever reason to reauthorize the "Account A" page, the other tokens will be invalidated, and the pages will stop working. From b6ad89f962d1d6bdd480488a30c24999f90ee849 Mon Sep 17 00:00:00 2001 From: markzegarelli Date: Thu, 12 Jan 2023 10:40:11 -0800 Subject: [PATCH 17/74] Developer Center 2.0 documentation (#4007) * Init * Intro edits * Draft complete * Apply suggestions from code review Co-authored-by: forstisabella <92472883+forstisabella@users.noreply.github.com> * Remove * Update src/partners/index.md Co-authored-by: forstisabella <92472883+forstisabella@users.noreply.github.com> * Update src/partners/index.md Co-authored-by: forstisabella <92472883+forstisabella@users.noreply.github.com> * Update src/partners/index.md Co-authored-by: forstisabella <92472883+forstisabella@users.noreply.github.com> * Clarifications from Sharan * No longer in Dev preview Co-authored-by: forstisabella <92472883+forstisabella@users.noreply.github.com> --- src/_data/sidenav/partners.yml | 38 +-- src/partners/destinations/authentication.md | 113 +++++++++ src/partners/destinations/build.md | 259 ++++++++++++++++++++ src/partners/destinations/index.md | 71 ++++++ src/partners/destinations/testing.md | 233 ++++++++++++++++++ src/partners/index.md | 141 +++++------ src/partners/sources.md | 46 ++++ 7 files changed, 811 insertions(+), 90 deletions(-) create mode 100644 src/partners/destinations/authentication.md create mode 100644 src/partners/destinations/build.md create mode 100644 src/partners/destinations/index.md create mode 100644 src/partners/destinations/testing.md create mode 100644 src/partners/sources.md diff --git a/src/_data/sidenav/partners.yml b/src/_data/sidenav/partners.yml index a2fccfa440..dcdd894e6a 100644 --- a/src/_data/sidenav/partners.yml +++ b/src/_data/sidenav/partners.yml @@ -3,25 +3,31 @@ sections: - section_title: Partners section: - path: /partners - title: Partners program overview + title: Overview - path: /partners/conceptual-model title: Conceptual model - - section_title: Building a subscription + - path: /partners/sources + title: Build a Source + - section_title: Build a Destination slug: partners/subscriptions section: - - path: /partners/subscriptions - title: Subscription overview - - path: /partners/subscriptions/build-functions - title: Building a Subscription Function - - path: /partners/subscriptions/build-webhook - title: Building a Subscription Webhook - - path: /partners/plugins - title: Building a Plugin - - path: /partners/streams - title: Building a Stream - - path: /partners/checklist - title: Public beta checklist - - path: /partners/enable-with-segment - title: Enable with OAuth + - path: /partners/destinations + title: Destination Overview + - path: /partners/destinations/build + title: Build a Destination + - path: /partners/destinations/testing + title: Test a Destination + - path: /partners/destinations/get-access + title: Get Access + - path: /partners/destinations/authentication + title: Authentication + # - path: /partners/plugins + # title: Building a Plugin + # - path: /partners/streams + # title: Building a Stream + # - path: /partners/checklist + # title: Public beta checklist + # - path: /partners/enable-with-segment + # title: Enable with OAuth - path: /partners/faqs title: Partners FAQs diff --git a/src/partners/destinations/authentication.md b/src/partners/destinations/authentication.md new file mode 100644 index 0000000000..18b4d4c1ae --- /dev/null +++ b/src/partners/destinations/authentication.md @@ -0,0 +1,113 @@ +--- +title: Authentication +--- +Most destinations require some sort of authentication. Segment's destination interface provides details about how customers need to authenticate with your destination to send data or retrieve data for dynamic input fields. + +## Basic Authentication + +Basic authentication is useful if your destination requires a username and password to authenticate. These are values that only the customer and the destination know. + +> success "" +> When scaffolding your integration, select Basic Auth from the auto-prompt or pass `--template basic-auth`. + +```js +const authentication = { + // the 'basic' authentication scheme tells Segment to automatically + // include the `username` and `password` fields so you don't have to. + // Segment will automatically do base64 header encoding of the username:password + scheme: 'basic', + + fields: { + username: { + label: 'Username', + description: 'Your username', + type: 'string', + required: true + }, + password: { + label: 'password', + description: 'Your password.', + type: 'string', + required: true + } + }, + + // a function that can test the user's credentials + testRequest: (request) => { + return request('https://example.com/api/accounts/me.json') + } +} + +const destination = { + // ...other properties + authentication, + + extendRequest({ settings }) { + return { + username: settings.username, + password: settings.password + } + } +} +``` + +## Custom Authentication + +Custom authentication is the most commonly used authentication among Segment destinations. It's what most “API Key” based authentication should use. You’ll likely need to define an `extendRequest` function to complete the authentication by modifying request headers with some authentication input fields. + +```js +const authentication = { + // the 'custom' scheme doesn't do anything automagically for you, but let's you + // define the behavior through input fields and `extendRequest`. + // this is what most API key-based destinations should use + scheme: 'custom', + + // a function that can test the user's credentials + testRequest: (request) => { + return request(`/accounts/me.json`) + }, + + // fields that are specific to authentication + fields: { + subdomain: { + type: 'string', + label: 'Subdomain', + description: 'The subdomain for your account, found in your user settings.', + required: true + }, + apiKey: { + type: 'string', + label: 'API Key', + description: 'Found on your settings page.', + required: true + } + } +} + +const destination = { + // ...other properties + authentication, + // we may explore a simple JSON representation that supports template strings + extendRequest: ({ settings }) => { + return { + prefixUrl: `https://${settings.subdomain}.example.com/api`, + headers: { Authorization: `Bearer ${settings.apiKey}` }, + responseType: 'json' + } + } +} +``` + +## OAuth 2.0 Managed + +If your API supports [OAuth 2.0](https://oauth.net/2/){:target="_blank"}, you can authenticate your destination's users with it. + +```js +const authentication = { + // the 'oauth-managed' authentication scheme tells Segment use the + // oauth credentials and endpoint that you provide to authenticate users. + scheme: 'oauth-managed', +} +``` + +When you receive access to the Developer Portal, find your integration, and navigate to the **OAuth settings** tab to configure the integration's OAuth details. \ No newline at end of file diff --git a/src/partners/destinations/build.md b/src/partners/destinations/build.md new file mode 100644 index 0000000000..849ea63320 --- /dev/null +++ b/src/partners/destinations/build.md @@ -0,0 +1,259 @@ +--- +title: Build a Destination +--- + + +This document describes in detail the steps necessary to create a new Actions-based destination using the Segment CLI. + +## Prerequisites + +Before you begin, consider the following prerequisites. + +### Security + +The security of customers and partners is a top priority at Segment. Before you begin building, review the [Acceptable Use Policy](https://segment.com/legal/acceptable-use-policy/), and keep in mind: + +- Follow a secure software-development lifecycle, which enables you to 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](mailto:security@segment.com) 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 + +You don't need to access a Segment dev environment to build an integration. You’ll test it locally on your machine. Destinations are written in TypeScript. For more information about TypeScript, see TypeScript's [documentation](https://www.typescriptlang.org/docs/){:target="_blank}. + +To work with Segment's actions repository, download and install the following: + - [node](https://nodejs.org/en/){:target="_blank"} + - [nvm](https://github.com/nvm-sh/nvm){:target="_blank"} + - [yarn](https://yarnpkg.com/){:target="_blank"} + +### Fork the repository + +Fork the `segmentio/action-destinations` repository, connect to NPM and Yarn, and ensure a compatible version of Node is installed. + +> info "" +> 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. + +```sh +git clone https://github.com//action-destinations.git +cd action-destinations +npm login +yarn login +# Requires node 14.17, optionally: nvm use 14.17 +yarn --ignore-engines --ignore-optional +yarn bootstrap +yarn build +yarn install +yarn test +``` + +## Create a destination + +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](https://github.com/segmentio/action-destinations/blob/main/packages/cli/README.md){:target="_blank"} to see a list of available commands. + +### Scaffold the new destination + +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](/docs/partners/destinations/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/`. The `init` command does not register or deploy the integration. + +### Cloud Mode Destination + +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: + +```js +import type { DestinationDefinition } from '@segment/actions-core' +import type { Settings } from './generated-types' + +const destination: DestinationDefinition = { + name: 'Test', + slug: 'actions-test', + mode: 'cloud', + + authentication: { + scheme: 'basic', + fields: { + username: { + label: 'Username', + description: 'Your Test username', + type: 'string', + required: true + }, + password: { + label: 'password', + description: 'Your Test password.', + type: 'string', + required: true + } + }, + testAuthentication: (request) => { + // Return a request that tests/validates the user's credentials. + // If you do not have a way to validate the authentication fields safely, + // you can remove the `testAuthentication` function, though discouraged. + } + }, + + extendRequest({ settings }) { + return { + username: settings.username, + password: settings.password + } + }, + + onDelete: async (request, { settings, payload }) => { + // Return a request that performs a GDPR delete for the provided Segment userId or anonymousId + // provided in the payload. If your destination does not support GDPR deletion you should not + // implement this function and should remove it completely. + }, + + actions: {} +} + +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 + +```js +import type { Settings } from './generated-types' +import type { BrowserDestinationDefinition } from '../../lib/browser-destinations' +import { browserDestination } from '../../runtime/shim' + +// Declare global to access your client +declare global { + interface Window { + sdkName: typeof sdkName + } +} + +// Switch from unknown to the partner SDK client types +export const destination: BrowserDestinationDefinition = { + name: 'BrowserExample', + slug: 'actions-browserexample', + mode: 'device', + + settings: { + // Add any Segment destination settings required here + }, + + initialize: async ({ settings, analytics }, deps) => { + await deps.loadScript('') + // initialize client code here + + return window.yourSDKName + }, + + actions: {} +} + +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 + +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: + +```js +const destination = { + // ...other properties + actions: { + postToChannel: { + // the human-friendly display name of the action + title: 'Post to Channel', + + // the human-friendly description of the action. supports markdown + description: 'Post a message to a Slack channel', + + // fql query to use for the subscription initially + defaultSubscription: 'type = "track"' + + // the set of fields that are specific to this action + fields: { + webhookUrl: { + label: 'Webhook URL', + description: 'Slack webhook URL.', + type: 'string', + format: 'uri', + required: true + }, + text: { + label: 'Message', + description: "The text message to post to Slack. You can use [Slack's formatting syntax.](https://api.slack.com/reference/surfaces/formatting)", + type: 'string', + required: true + } + }, + + // the final logic and request to send data to the destination's API + perform: (request, { settings, payload }) => { + return request.post(payload.webhookUrl, { + responseType: 'text', + json: { + text: payload.text + } + }) + } + } + } +} +``` + +### Actions best practices + +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 + +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 + +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 + +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. + +## Write tests + +Testing ensures that your destination functions the way you expect. For information on testing, see [Test your destination](/docs/partners/destinations/testing). + diff --git a/src/partners/destinations/index.md b/src/partners/destinations/index.md new file mode 100644 index 0000000000..164686e75a --- /dev/null +++ b/src/partners/destinations/index.md @@ -0,0 +1,71 @@ +--- +title: Destinations +--- + +[Destination Actions](/docs/connections/destinations/actions/) allow users to create subscriptions, or sets of conditions in which data is sent to the destinations, and data mappings, which format that data for the destination tool. Segment watches for data that matches the conditions you create (triggers) for the subscription, and when the conditions are met, uses an explicit mapping to transform the incoming data to an output format that your destination can use. + +## Development process + +Follow the high-level steps below to create your destination. + +### Become a partner + +Sign up for the [Segment Select Partner Program](https://segment.com/partners/integration/). During the sign-up process, you’ll agree to the [Segment Partner Program Agreement](https://segment.com/legal/partnersagreement/) and [Privacy Policy](https://segment.com/legal/privacy/). + + +### Plan your integration + +Before you begin development, consider the following points: + +1. Decide the type of destination you want to build. Developer Center supports building cloud-mode and device-mode web destinations. Segment recommends building a cloud-mode destination, because data is sent to Segment prior to going to your API, so customers can take advantage of Segment features like filters, transformations, and replays. You can learn more [here](https://segment.com/docs/connections/destinations/#connection-modes). Developer Center does not support building device-mode mobile destinations. Segment recommends building a plugin to get information like session ID from the device. + +2. Spec out the integration. If you want some guidance, you can use this [template](https://docs.google.com/document/d/1dIJxYge9N700U9Nhawapy25WMD8pUuey72S5qo3uejA/edit#heading=h.92w309fjzhti){:target="_blank"}, which will prompt you to think about the connection mode of the destination, the method of authentication, the settings, and the Actions and default Field Mappings that you want to build. + +3. Join the Segment Partners Slack workspace. + +### Build your integration + +1. You don't need to access a Segment dev environment to build an integration. You’ll test it locally on your machine. Destinations are written in TypeScript. For more information about TypeScript, see TypeScript's [documentation](https://www.typescriptlang.org/docs/){:target="_blank}. + +2. To work with Segment's actions repository, download and install the following: + - [node](https://nodejs.org/en/){:target="_blank"} + - [nvm](https://github.com/nvm-sh/nvm){:target="_blank"} + - [yarn](https://yarnpkg.com/){:target="_blank"} + + +3. To test your integration: + + - For cloud-mode destinations, follow these instructions: [Test Cloud Destinations](/docs/partners/destinations/testing). + - If you are building a device-mode destination, see the [Test Browser Destinations](#). + +4. When you have questions, ask in the Segment Partners Slack workspace. + +For more information, see [Build a Destination](/docs/partners/destinations/build). + +### Submit a pull request + +Once you’ve finished making your changes, added unit tests for new functionality, and tested end-to-end using the local server, you can [create a pull request](https://github.com/segmentio/action-destinations/compare){:target="_blank”}. + +- When you create a pull request, include a description of the changes made and why. This will help during the code review process. +- Verify that you have thoroughly tested your changes by selecting the appropriate checkboxes. +- A Segment developer will review the PR. They may ask for changes to be completed before the PR can be merged. Once all changes are made, the Segment developer will approve the PR. +- When you submit a PR, the Segment team is automatically notified. The turnaround time for reviews may take up to 2-3 business days. + + +Your PR is merged! + +- Congratulations! Once your PR is merged by a Segment developer, they will deploy your changes and notify you when it’s publicly available. If the destination is in private beta, Segment provides a link to access your destination. Once the destination is ready for general availability and has been approved, the destination will be visible in the integrations catalog. +- The Developer Center deploys on Wednesdays for all non-emergency changes. Changes should be approved and merged by Tuesday 5pm Pacific time to make the Wednesday release. + + +### Join the Developer Portal + +After your code is deployed, you'll receive an invitation to join the Segment Developer Portal. You'll use this portal to provide additional metadata for the Segment catalog. + +### Write documentation + +Documentation is integral to enabling Segment's users to self-serve and onboard with your integration. Segment's documentation team will work with you during this part of the process to ensure your documentation matches the Segment style and is as instructive as possible. + +### Release your Destination + +Once documentation is complete, your Destination will enter Public Beta status. Destinations remain in Public Beta status for a minimum of two weeks, and until the destination is added and configured by two users. Once these criteria are met, the destination moves to General Availability. diff --git a/src/partners/destinations/testing.md b/src/partners/destinations/testing.md new file mode 100644 index 0000000000..2eb4c7e00a --- /dev/null +++ b/src/partners/destinations/testing.md @@ -0,0 +1,233 @@ +--- +title: Test Your Destination +--- + +## Local End-to-end Testing + +To test a destination action locally, you can create a local HTTP server with the Actions CLI. + +```sh +# For more information, add the --help flag +./bin/run serve +``` +> success "Customize the port number" +> The default port is set to `3000`. To use a different port, you can specify the `PORT` environment variable (for example, `PORT=3001 ./bin/run serve`). + +After you run the `serve` command, select the destination you want to test. + +To test a specific destination action, you can send a Postman or cURL request with the following URL format: `https://localhost:/`. The CLI provides a list of eligible URLs when the server is running. + +### Example + +The following is an example of a cURL command for the `search` action of the `google-analytics-4` destination. The `payload`, `settings`, `auth`, and `features` values are all optional in the request body. Local testing requires that you pass the action's required fields in the `payload` object. + +```sh +curl --location --request POST 'http://localhost:3000/search' \ +--header 'Content-Type: application/json' \ +--data '{ + "payload": { + "client_id": "", + "search_term": "" + }, + "settings": { + "measurementId": "", + "apiSecret": "" + }, + "auth": { + "accessToken": "", + "refreshToken": "" + } +}' +``` + +### Testing Batches + +You can test Actions-based destinations that support batching (those that have a `performBatch` handler) locally. Test events for batch-supporting destinations should include `payload` as an array, and not an object. Here is an example of `webhook`'s `send` action, with a batch `payload`. + +```sh +curl --location --request POST 'http://localhost:3000/send' \ +--header 'Content-Type: application/json' \ +--data '{ + "payload": [{ + "url": "https://www.example.com", + "method": "PUT", + "data": { + "cool": true + } + }], + "settings": {}, + "auth": {}, + "features": {} +}' +``` +## Unit Testing + +When you build a destination action, Segment recommends that write unit and end-to-end tests to ensure your action works as intended. Tests run on every commit in GitHhub Actions. Pull requests that don't include relevant tests are not approved. + +Unit tests behave more like integration tests in that you test not only the `perform` operation/unit, but also how events and mappings are transformed and validated. + +Run tests for all cloud destinations with `yarn cloud test` or target a specific destination with the `--testPathPattern` flag: + +``` +yarn cloud test --testPathPattern=src/destinations/sendgrid +``` + +### Mocking HTTP Requests + + +While testing, want to avoid hitting external APIs. Segment uses `nock` to intercept any outbound requests before they hit the network. + +### Examples + +#### Test events and mapping + +```js +import nock from 'nock' +import { createTestIntegration, StatsClient } from '@segment/actions-core' +import SendGrid from '../index' + +const statsClient = {} as StatsClient +const tags = ['integration:actions-sendgrid'] + +const testDestination = createTestDestination(SendGrid) + +const SENDGRID_API_KEY = 'some random secret' + +describe('SendGrid', () => { + describe('createList', () => { + it('should validate action fields', async () => { + try { + await testDestination.testAction('createList', { + settings: { apiKey: SENDGRID_API_KEY }, + skipDefaultMappings: true + }) + } catch (err) { + expect(err.message).toContain("missing the required field 'name'.") + } + }) + + it('should work', async () => { + nock('https://api.sendgrid.com/v3') + .post('/marketing/lists', { name: 'Some Name' }) + .reply(200) + + await testDestination.testAction('createList', { + mapping: { name: 'Some Name' }, + settings: { apiKey: SENDGRID_API_KEY }, + features: { my_feature: true }, + statsContext: { statsClient, tags } + }) + }) + }) +}) +``` + +#### Testing authentication scheme with unit tests + +```js +// ... + +describe('SendGrid', () => { + // ... + + describe('authentication', () => { + it('should validate api keys', async () => { + try { + await testDestination.testAuthentication({ apiKey: 'secret' }) + } catch (err) { + expect(err.message).toContain('API Key should be 32 characters') + } + }) + + it('should test that authentication works', async () => { + nock('https://api.sendgrid.com/v3') + .get('/user/profile') + .matchHeader('authorization', `Bearer some valid super secret api key`) + .reply(200, {}) + + await expect(testDestination.testAuthentication(settings)).resolves.not.toThrow() + }) + it('should test that authentication fails', async () => { + nock('https://api.sendgrid.com/v3') + .get('/user/profile') + .reply(403, { + errors: [{ field: null, message: 'access forbidden' }] + }) + + try { + await testDestination.testAuthentication({ apiKey: `nope this is an invalid key` }) + } catch (err) { + expect(err.message).toContain('Credentials are invalid') + } + }) + }) +}) +``` + +## Snapshot Testing + +Snapshot tests help developers understand how their changes affect the request body and the downstream tool. In `action-destinations`, they are automatically generated with both the `init` and `generate:action` CLI commands - the former creating destination-level snapshots and the latter creating action-level snapshots. These tests can be found in the `snapshot.test.ts` file under the `__tests__` folder. + +The `snapshot.test.ts` file mocks an HTTP server using `nock`, and generates random test data (w/ `Chance`) based on the destination action's fields and corresponding data type. For each destination action, it creates two snapshot tests - one for all fields and another for just the required fields. To ensure deterministic tests, the `Chance` instance is instantiated with a fixed seed corresponding to the destination action name. + +Once the actions under a new destination are complete, developers can run the following command to generate a snapshot file (`snapshot.test.ts.snap`) under `/__tests__/snapshots/`. + +``` +yarn jest --testPathPattern='./packages/destination-actions/src/destinations/' --updateSnapshot +``` + +## Code Coverage + +Code coverage is automatically collected upon completion of `yarn test`. Results may be inspected by examining the HTML report found at `coverage/lcov-report/index.html`, or directly in your IDE if _lcov_ is supported. + + +## Actions Tester + +To see a visual representation of the settings/mappings fields Segment provides a tool to preview and execute simulated actions mappings against your in development destination. + + +### Getting started + +For cloud action destinations, run one of the following commands, depending on the destination type, inside the directory where you have cloned the `actions-destinations` repository: + +| Type | Command | +| ------- | ---------------------------------------------------------------------------------------- | +| Cloud | `./bin/run serve` | +| Browser | `./bin/run serve --directory ./packages/browser-destinations/src/destinations --browser` | + + +You can either select the new action destination from the command line menu, or optionally pass it with an environment variable. + +The command will return some text which includes a URL to the action tester UI. Click or copy/paste this text into a browser to get started. If you're not logged in to the Segment App, log in with your Segment credentials. + +### Using Actions Tester + +The Actions tester UI is split into 3 main areas: + +#### The Segment 'Test Event' + +Think of this as the 'incoming' data sent from the customer's 'source' through the Segment data plane, and eventually to your actions destination. + +#### Settings / Mappings + +The middle pane provides an area that allows you to preview a representation of the Segment UI for your destination's configuration. The layout, including the order may not be 100% representative of how Segment will render the destination's user interface. This serves as a useful playground for determining the ways in which mappings are configurable by the end user, and the impact your choices in the field definitions have on user experience. + +The settings pane shows a representation of the 'global' settings available for your destination. + +The mappings pane (which mappings are shown is determined by the dropdown above) shows a representation of the individual mappings (as well as any defaults you have specified) for a given action. + +#### Mappings output + +The final pane is a JSON representation of the test event data after it is mapped by your destination. This is updated in real time as you make changes to the test event or the mappings. This data is representative of the data that is your destination provides on the `payload` property of the perform method at execution time. + +### Editing field definitions + +While working on your destination's definitions in TypeScript, if you have action tester running locally, the tester UI updates with settings or mapping field changes without the need to restart the local server component. There is a slight delay to account for the local bundling process. + +#### Test your action + +The Actions tester enables a simulated test of the action environment. Click **Test** in the lower right corner to trigger the `perform` method of your action and pass it the `settings` and `payload` generated in the testing UI. This allows you to debug the perform method, as well as validate any responses from your API in the output panel. + +The output panel behaves in two 'modes'. The first is `immediate` failures. If your API call could not be completed due to invalid url, credentials, etc, the pane will displays any debug information in the client. + +When you make a successful API call, the Actions Tester shows both the request and response objects that the actions runtime uses internally to track your event. These are persisted across individual calls. If multiple calls appear and this is not desired behavior, reload the browser instance. \ No newline at end of file diff --git a/src/partners/index.md b/src/partners/index.md index e1f5f2b320..fef76839d9 100644 --- a/src/partners/index.md +++ b/src/partners/index.md @@ -1,125 +1,118 @@ --- title: Developer Center Overview --- +Welcome! Here are the steps you’ll follow to build an integration on Dev Center 2.0, launch your destination to Private Beta so customers can test it, and then launch it as Public in the Segment catalog. -{% include content/dev-center-note.md %} +{% comment %} +Before you continue, read the [Code of Conduct](./CODE_OF_CONDUCT.md). By contributing to this project, you are expected to uphold this code. +{% endcomment %} +## Build on Segment -## Building on Segment +Over 19,000 companies use Segment as their central hub for collecting and synthesizing first-party customer data. Customers use Segment [sources](/docs/connections/sources/) to collect data across all their properties (for example, web, mobile, CRMs, or email) and send this data into [destinations](/docs/connections/destinations/) (SaaS tools, internal databases or queues, or a data warehouse) to perform analytics, run marketing campaigns and much more. -Over 19,000 companies use Segment as their central hub for collecting and synthesizing first-party customer data. Customers use Segment [Sources](/docs/connections/sources/) to collect data across all their properties (web, mobile, CRMs, email etc.) and send this data into [Destinations](/docs/connections/destinations/) (SaaS tools, internal databases or queues, or a data warehouse) to perform analytics, run marketing campaigns and much more. +### Integration types -Below is a sample screenshot of a customer's Segment dashboard, showing all their sources of data on the left, and destinations for their data on the right. +Segment provides two different integration types to support bringing your data into Segment, and sending your data downstream to other third-party tools. -![](images/overview.png) +#### Sources -Segment's core feature is the Connections Catalog: +[Sources](/docs/connections/sources/) bring users' first-party data into Segment. While there are several *types* of sources (for example, web or server libraries, mobile integrations, and Cloud), the Developer Center enables you to build your own [*Cloud Event*](/docs/connections/sources/#event-cloud-sources) sources. These sources enable users to import data directly from your application into Segment. -![](images/catalog.gif) +#### Destinations -Customers discover your Sources and Destinations using the Connections Catalog and can enable them from there. +[Destinations](/docs/connections/destinations/) send data to other tools for processing or analysis. For example, a Segment user may want to send their data to your advertising platform or analytics tool. To accomplish this, they'll connect your Segment destination to their workspace. -The development process is simple: +All new Segment Destinations are built on the [Actions framework](/docs/connections/destinations/actions/), which enables a simplified build experience for you and a more straightforward configuration experience for your users. -1. Understand Segment's [Conceptual Model](/docs/partners/conceptual-model) and [Spec](/docs/connections/spec). -2. Follow Segment's security guidance. -3. Request [access to the Segment Developer Center](https://segment.com/partners/developer-center/). -4. Create an App. -5. Build and test your Component(s). -6. Publish documentation. -7. Submit your App for review. -8. Launch into _Public Beta_! +## Development process -### 1. Understand Segment's Conceptual Model +To develop your integration in the Developer Center, complete the following steps: +1. [Become a Segment Partner](#become-a-segment-partner) +2. [Understand Segment's conceptual model and Spec](#understand-segments-conceptual-model-and-spec) +3. [Follow Segment's security guidance](#follow-segments-security-guidance) +4. [Request access to the Segment Developer Center](#request-access-to-the-segment-developer-center) +5. [Create your integration](#create-your-integration) +6. [Write your integration's documentation](#write-your-integrations-documentation) -It's important to understand Segment's [Conceptual Model](/docs/partners/conceptual-model) to begin planning your integration. This will help you understand how data will flow from or into your integration. -### 2. Follow Segment's security guidance +### Become a Segment Partner -Security for both customers and partners is a priority at Segment. Before you start building on the Developer Center, review the [Acceptable Use Policy](https://segment.com/legal/acceptable-use-policy/) and ensure you're following the below guidance: +Sign up for the [Segment Select Partner Program](https://segment.com/partners/integration/). During the sign-up process, you’ll agree to the [Segment Partner Program Agreement](https://segment.com/legal/partnersagreement/) and [Privacy Policy](https://segment.com/legal/privacy/). -- Follow a secure software-development lifecycle, which enables you to create code that is safe for Segment customers and their end users, and that enables you to 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 its data lifecycle, including creation, handling, transporting, storing and destruction. -- If you suspect a security event, incident or breach while working on this project or afterward, contact [Segment Security](mailto:security@segment.com?subject=Developer%20Center%20Security) for assistance with your investigation and communications -- Practice modern and common-sense security for any scenario not explicitly stated - -### 3. Request Access - -During _Developer Preview_, you will need to [request access to Developer Center](https://segment.com/partners/developer-center/). A Segment account is required for this step. - -Segment receives a large volume of requests so please include a valid company website and email address, answer all questions with details about integration's use case as well as highlighting specific customer requests to expedite the approval process. - -### 4. Create your App +### Understand Segment's conceptual model and Spec -Once approved, you can create your first [App](/docs/partners/conceptual-model/#apps). This represents a tile in the [Segment catalog](https://segment.com/catalog/) irrespective of which [Component](/docs/partners/conceptual-model/#components) type you choose to build so it should reflect your tool's name (for example, Zendesk Chat, Zendesk Sell). +Segment's [Conceptual Model](/docs/partners/conceptual-model) is a high-level overview of how Segment works and explains how your integration fits into the Segment catalog. +The [Segment Spec](/docs/connections/spec) provides best practices for the specific data you should capture and the best way to format that data based on your use case. The Spec outlines the semantic definition of the customer data that Segment captures across all its libraries and APIs, and will be a main building block for your integration. -### 5. Build & Test +### Follow Segment's security guidance -Now you can start building! Depending on your use case you can build a: +Security for both customers and partners is a priority at Segment. Before you start building on the Developer Center, review the [Acceptable Use Policy](https://segment.com/legal/acceptable-use-policy/) and ensure you're following these guidelines: -- [Subscription](/docs/partners/subscriptions) -- [Plugin](/docs/partners/plugins) -- [Stream](/docs/partners/streams) +- Follow a secure software-development lifecycle, which enables you to create code that is safe for Segment customers and their end users, and that enables you to 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 its data lifecycle, including creation, handling, transporting, storing and destruction. +- If you suspect a security event, incident or breach while working on this project or afterward, contact [Segment Security](mailto:security@segment.com?subject=Developer%20Center%20Security) for assistance with your investigation and communications +- Practice modern and common-sense security for any scenario that is not explicitly stated. +### Request access to the Segment Developer Center -No matter which Component you decide to build, make it aligns with the [Segment Spec](/docs/connections/spec). This is a critical component in preserving logical continuity between disparate writers and readers of data. If you encourage customers to break the spec, you are breaking the promise of Segment, and is grounds for removal from the catalog. +Segment provides access to the developer on request. [Click here](https://segment.com/partners/developer-center/){:target="_blank"} to request access. A Segment account is required for this step. -To provide a positive experience for mutual customers, it's important to test integrations with the tooling provided by the Developer Center. You can also use your Segment sandbox to polish the experience your end users will see. +Segment receives a large volume of requests so please include a valid company website and email address, answer all questions with details about integration's use case as well as highlighting specific customer requests to expedite the approval process. -### 6. Document +### Create your integration -Finally, make sure you prepare documentation and relevant marketing material for easy discovery and reference. Provide the following documentation about your integration by making a copy of the below templates: +Follow the steps to build your [source](/docs/partners/sources) or [destination](/docs/partners/destinations). -1. Documentation [hosted by Segment](https://segment.com/docs/) for [Subscription / Plugin](https://hackmd.io/t7amLXluS7-39rg7ARZgSA) or [Stream](https://hackmd.io/TV8cZR6tRnKwGtkvSexeag) -2. Documentation for [the Segment catalog](https://segment.com/catalog/) using [this template](https://docs.google.com/document/d/1kKvqYtZeDPnBjvCrtQSuO3BBH70b_CLO13hYrYIOOtA/edit)) -3. Documentation hosted on your own website about your new Segment integration -4. Draft blog post announcing your new integration +### Write your integration's documentation +Documentation is integral to enabling Segment's users to self-serve and onboard with your integration. Segment's documentation team will work with you during this part of the process to ensure your documentation matches the Segment style and is as instructive as possible. -### 7. Submission -You can submit your Component in the Developer Center for review. Segment aims to respond to your submission within two business days to kickstart the review process. -Segment tests your integration and reviews your documentation and marketing material. To keep this review feedback loop short, make sure that your integration: +### Submit your integration for review -- Adheres to the [Segment Spec](/docs/connections/spec/) -- Adheres to your published documentation -- Supports high data volumes -- Meets all [launch requirements](/docs/partners/#launch-requirements) +Before users can go hands on with your integration, a review by Segment engineers is required to ensure the integration meets security and usability standards. +#### Destinations -## Launch Requirements +To submit your destination for review, follow the destination-specific instructions [here](/docs/partners/destinations#submit-a-pull-request). -See the [Public Beta Checklist](/docs/partners/checklist) for a detailed checklist used by the Segment team to review and approve the launch of your integration. +#### Sources -In a nutshell you need: +To submit your source for review, complete the steps described in the Developer Portal, and click **Submit for review**. -1. A working integration tested end-to-end. Follow your own documentation and run through the experience as a mutual customer. +{% comment %} +## Provide integration metadata for the catalog -2. Complete the fields under the _App Info_ tab with your App including both _Basic Info_ and _Launch Info_. This includes: +Send the following information to partner-support@segment.com using the below template: - - **Segment Documentation:** Using these templates ([subscription](https://hackmd.io/t7amLXluS7-39rg7ARZgSA?both=)/[stream](https://hackmd.io/TV8cZR6tRnKwGtkvSexeag), document how mutual customers can use your integration. - - **Your Documentation:** Similar to the above but hosted on your own website. - - **Catalog Details:** Details for the [catalog](https://segment.com/catalog) material including screenshots by making a copy of [this template](https://docs.google.com/document/d/1kKvqYtZeDPnBjvCrtQSuO3BBH70b_CLO13hYrYIOOtA/copy). - - **Integrations / Partners Page:** Add your Segment integration to your integrations or partners page. - - **Blog Post:** Publish a launch blog post about your integration, like [this](https://www.kustomer.com/blog/kustomer-segment-integration/). Make sure you share it on Twitter and LinkedIn too! (If you don't have a blog, an email is okay) +Please find the below info for _Name of integration_ Catalog entry. -Be sure to use Segment's [brand kit](https://brandfolder.com/segment/press-kit) for logos, and Segment's [UTM guide](https://docs.google.com/document/d/1L0MHYdF2SYaMMiodQCYlZELF7pN0TXiZbD22nnlGhEk/copy) any time you link to a Segment page. +- **Name:** _Name of integration_ +- **Link to your most recent PR on the actions-destination repository:** _Link to PR_ +- **Description:** _Brief description of your integration, up to ~500 characters. Descriptions can include formatted text and lists. Optionally, you can include up to two images, such as screenshots, with or without captions._ +- **Website:** _Website for your company or product, for example https://amplitude.com_ +- **Categories:** _Select a primary and (optional) secondary category where your integration will appear in the catalog._ + - Choose from: A/B Testing, Advertising, Analytics, Attribution, CRM, Customer Success, Deep Linking, Email Marketing, Enrichment, Feature Flagging, Heatmaps & Recordings, Live chat, Marketing Automation, Performance Monitoring, Personalization, Raw Data, Referrals, Security & Fraud, SMS & Push Notifications, Surveys, Tag Managers, Video +- **Logo:** _Your logo includes the name of your company. A horizontal lockup is best. File type must be SVG._ +- **Mark:** _Your mark is square and does not include the name of your company. File type must be SVG._ +- **Customer support email address:** _Email address for customer support inquiries. This email address will not be surfaced to users of the integration; instead it will be used by Segment customer support. Should Segment receive an inquiry about the integration from a Segment customer, Segment support will send the inquiry to this email address._ -You can contact Segment Partner Support at [partner-support@segment.com](mailto:partner-support@segment.com) once you have all these elements ready for review. Once approved, your Destination goes live on the Catalog in Public Beta. +## Release to Private Beta for customer testing -## Post Launch +During Private Beta, the integration will not be publicly listed in the catalog. You and your customers can access the catalog page using a special URL: `https://app.segment.com/goto-my-workspace/destinations/catalog/${destination-slug}` (This will direct users to the install page in their workspace). -Congratulations on launching your integration in Public Beta - welcome aboard! Here are the next steps to move out from Public Beta to Public: +1. Open the install URL for your integration (https://app.segment.com/goto-my-workspace/destinations/catalog/${destination-slug}) and verify that the catalog entry renders correctly. -1. Implement the [Enable with Segment OAuth](/docs/partners/enable-with-segment) button - this makes it much easier for mutual customers to get started with your integration! -2. Onboard at least three customers to actively use your integration +2. Invite one or more customers to test the integration by giving them the URL. At least one customer must verify that the destination works before it becomes available to the public. -Bonus points if you join the [Segment Select](/docs/partners/#segment-select) Partner Program! +## Release to Public in the Segment catalog -## Segment Select +1. Once at least one customer successfully uses the integration, contact the Developer Center team, who will make your destination Public. When a destination is Public, any Segment customer can find it in the catalog and it will be featured on the New & Noteworthy page. +2. Write a blog post for your company’s blog, write a [recipe](https://segment.com/recipes/) to help customers solve a specific problem using your Integration, and/or work with Segment's Marketing team to be featured in the Segment blog. -You can taking advantage of Segment's available partner opportunities by [joining the Segment Select Partner Program](https://segment.com/partners/integration/#module-5-benefits). +3. Maintain your integration: fix bugs, update it if your APIs change, and add functionality as requested by customers. -By becoming a Segment Select partner, you have access to sales support, technical training, and personalized co-marketing opportunities. [Learn more about the program details here.](https://assets.ctfassets.net/9u2t3ap6pctb/3NPVQDweiX0l8Z2edugwIr/d09ea71f04913f3189514b7d2df57d36/Segment_Select_Partner_Program_One_Pager.pdf) +{% endcomment %} \ No newline at end of file diff --git a/src/partners/sources.md b/src/partners/sources.md new file mode 100644 index 0000000000..f9d5071794 --- /dev/null +++ b/src/partners/sources.md @@ -0,0 +1,46 @@ +--- +title: Build a Source +--- + +Sources send data into Segment, and serve as the origin for your data. Segment empowers companies of all sizes to use their customer data to grow their business. When you create a Segment source for your organization, you enable customers to use data from your tool wherever it's most useful to them. + +## Get access and log in + +Before you begin, you need access to the Developer Portal. To access the Developer portal: + +1. Apply to become a [Segment Select Partner](https://segment.com/partners/integration/){:target="_blank"}. +2. Once approved, you'll receive an invitation to join the Segment Developer Portal. +3. Log in to Segment, navigate to the User dropdown in the top right of the screen, and click [**Developer Portal**](https://app.segment.com/dev-portal){:target="_blank"}. + +## Build the Source + +1. Once in the Developer Portal, navigate to **Integrations** and click **Build integration** > **Build a source** + +2. On the Source setup screen, enter a name and slug for your source, and click **Create source**. + +3. From the Integrations screen, click on the source you just created. You should see your source in **Private building** status. + +4. Select the **Catalog info** tab and add relevant metadata and catalog information for your source. + +The code for your source should follow the format of Segment's [HTTP API](/docs/connections/sources/catalog/libraries/server/http-api/). + +## Test your Source + +1. From your source's page in the Developer Portal, navigate to **Settings** > **Add to workspace**. From the dropdown, select the Segment workspace you'll use to test your source, and click **Add to workspace**. The selected workspace will open in a new tab. + +When the selected workspace opens in a new tab, with your source's configuration page visible, copy the workspace ID, and add it to your source. + +Use the [Source Debugger](/docs/connections/sources/debugger/) to observe inbound data when you generate events in your source. For example, if your source sends email data to Segment, you should: + +- Create an email campaign that includes one hyperlink and an unsubscribe option +- Send the email to yourself +- When you receive the email + - Open it + - Click the link + - Unsubscribe + +Check the Source Debugger to verify that the events arrive and are formatted according to the Segment Spec. + +## Launch your source + +When you've verified that your source sends the correct information, submit it for review. The Segment team will review your source's functionality, catalog metadata, and documentation. If your source is approved, it will appear in the Segment catalog, marked with a "beta" badge for a period of two weeks. After this period, the source is considered generally available. From 7edc2f3857f413c26080d091c0fc0758442826b8 Mon Sep 17 00:00:00 2001 From: stayseesong Date: Thu, 12 Jan 2023 17:29:54 -0800 Subject: [PATCH 18/74] edits [netlify-build] --- src/_data/sidenav/strat.yml | 4 + .../catalog/libraries/server/node/index.md | 12 +-- .../libraries/server/node/migration.md | 90 +++++++++++++++++++ 3 files changed, 100 insertions(+), 6 deletions(-) create mode 100644 src/connections/sources/catalog/libraries/server/node/migration.md diff --git a/src/_data/sidenav/strat.yml b/src/_data/sidenav/strat.yml index 6e1394a567..70b8f9df36 100644 --- a/src/_data/sidenav/strat.yml +++ b/src/_data/sidenav/strat.yml @@ -197,3 +197,7 @@ sections: title: Analytics-Node.js - path: /connections/sources/catalog/libraries/server/node/quickstart/ title: Quickstart Node.js + - path: /connections/sources/catalog/libraries/server/node/classic/ + title: Analytics-Node.js Classic + - path: /connections/sources/catalog/libraries/server/node/migration/ + title: Analytics-Node.js Migration Guide diff --git a/src/connections/sources/catalog/libraries/server/node/index.md b/src/connections/sources/catalog/libraries/server/node/index.md index 523b89d0dc..11515a57b5 100644 --- a/src/connections/sources/catalog/libraries/server/node/index.md +++ b/src/connections/sources/catalog/libraries/server/node/index.md @@ -19,7 +19,7 @@ All of Segment's server-side libraries are built for high-performance, so you ca > warning "" > Make sure you're using a version of Node that's 14 or higher. -1. Run the following to add Segment's Node library module to your `package.json`. +1. Run the relevant command to add Segment's Node library module to your `package.json`. ```bash # npm @@ -43,7 +43,7 @@ All of Segment's server-side libraries are built for high-performance, so you ca Be sure to replace `YOUR_WRITE_KEY` with your actual **Write Key** which you can find in Segment by navigating to: **Connections > Sources** and selecting your source and going to the **Settings** tab. - This creates an instance of `Analytics` that you can use to send data to Segment for your project. The default initialization settings are production-ready and queue 20 messages before sending any requests. In development you might want to use [development settings](/docs/connections/sources/catalog/libraries/server/node/#development). + This creates an instance of `Analytics` that you can use to send data to Segment for your project. The default initialization settings are production-ready and queue 20 messages before sending any requests. ## Basic tracking methods @@ -96,7 +96,7 @@ Field | Details `userId` _String, optional_ | The ID for this user in your database. _Note: at least one of `userId` or `anonymousId` must be included in any identify call._ `anonymousId` _String, optional_ | An ID associated with the user when you don't know who they are (for example, [the anonymousId generated by `analytics.js`](/docs/connections/sources/catalog/libraries/website/javascript/#anonymous-id)). _Note: You must include at least one of `userId` or `anonymousId` in all identify calls._ `traits` _Object, optional_ | A dictionary of [traits](/docs/connections/spec/identify#traits) you know about the user. Things like: `email`, `name` or `friends`. -`timestamp` _Date, optional_ | A JavaScript date object representing when the identify took place. If the identify just happened, leave it out and we'll use the server's time. If you're importing data from the past make sure you to send a `timestamp`. +`timestamp` _Date, optional_ | A JavaScript date object representing when the identify took place. If the identify just happened, leave it out as Segment uses the server's time. If you're importing data from the past make sure to send a `timestamp`. `context` _Object, optional_ | A dictionary of extra [context](https://segment.com/docs/connections/spec/common/#context) to attach to the call. _Note: `context` differs from `traits` because it is not attributes of the user itself._ Find details on the **identify method payload** in Segment's [Spec](/docs/connections/spec/identify/). @@ -359,12 +359,12 @@ analytics.on('track', (ctx) => console.log(ctx)) ``` ## Plugin architecture -When you develop against Analytics 2.0, the plugins you write can augment functionality, enrich data, and control the flow and delivery of events. From modifying event payloads to changing analytics functionality, plugins help to speed up the process of getting things done. +When you develop against [Analytics.js 2.0](/docs/connections/sources/catalog/libraries/website/javascript/), the plugins you write can augment functionality, enrich data, and control the flow and delivery of events. From modifying event payloads to changing analytics functionality, plugins help to speed up the process of getting things done. Though middlewares function the same as plugins, it's best to use plugins as they are easier to implement and are more testable. ### Plugin categories -Plugins are bound by Analytics 2.0 which handles operations such as observability, retries, and error handling. There are two different categories of plugins: +Plugins are bound by Analytics.js 2.0 which handles operations such as observability, retries, and error handling. There are two different categories of plugins: * **Critical Plugins**: Analytics.js expects this plugin to be loaded before starting event delivery. Failure to load a critical plugin halts event delivery. Use this category sparingly, and only for plugins that are critical to your tracking. * **Non-critical Plugins**: Analytics.js can start event delivery before this plugin finishes loading. This means your plugin can fail to load independently from all other plugins. For example, every Analytics.js destination is a non-critical plugin. This makes it possible for Analytics.js to continue working if a partner destination fails to load, or if users have ad blockers turned on that are targeting specific destinations. @@ -455,7 +455,7 @@ await analytics.register(pluginA, pluginB, pluginC) Deregister a plugin by using: ```js -await analytics.dereigsrer("pluginNameA", "pluginNameB") // takes strings +await analytics.deregister("pluginNameA", "pluginNameB") // takes strings ``` ## Selecting Destinations diff --git a/src/connections/sources/catalog/libraries/server/node/migration.md b/src/connections/sources/catalog/libraries/server/node/migration.md new file mode 100644 index 0000000000..ac5d055220 --- /dev/null +++ b/src/connections/sources/catalog/libraries/server/node/migration.md @@ -0,0 +1,90 @@ +--- +title: Analytics for Node.js Migration Guide +repo: analytics-next +strat: node-js +--- + +> info "" +> This version of Analytics for Node.js is in beta and Segment is actively working on this feature. Segment's [First-Access and Beta terms](https://segment.com/legal/first-access-beta-preview/) govern this feature. + +If you're using the [classic version of Analytics Node.js](/docs/connections/sources/catalog/libraries/server/node/classic), follow these steps to upgrade to the [latest version of Analytics Node.js](/connections/sources/catalog/libraries/server/node/). + +1. Change the named imports. + +
Before: + ```javascript + import Analytics from 'analytics-node' + ``` + + After: + ```javascript + import { Analytics } from '@segment/analytics-node' + ``` +2. Change instantiation to have an object as the first argument. + +
Before: + ```javascript + var analytics = new Analytics('YOUR_WRITE_KEY'); + ``` + + After: + ```javascript + const analytics = new Analytics({ writeKey: '' }) + ``` +3. Change flushing to [graceful shutdown](/docs/connections/sources/catalog/libraries/server/node//#graceful-shutdown). + +
Before: + ```javascript + await analytics.flush(function(err, batch) { + console.log('Flushed, and now this program can exit!'); + }); + ``` + + After: + ```javascript + await analytics.closeAndFlush() + ``` + +### Differences to note between the classic and updated version + +* The callback call signature changed. + +
Before: + ```javascript + (err, batch) => void + ``` + + After: + ```javascript + (err, ctx) => void + ``` +* The `flushAt` configuration option changed to `maxEventsInBatch`. + +#### Removals +The updated Analytics Node.js removed these configuration options: +- `enable` +- `errorHandler` (see the docs on [error handling](/docs/connections/sources/catalog/libraries/server/node//#error-handling) for more information) + +The updated Analytics Node.js library removed undocumented behavior around `track` properties + +Before: + +```javascript +analytics.track({ + ... + event: 'Ultimate Played', + myProp: 'abc' +}) +``` + +After: + +```javascript +analytics.track({ + ... + event: 'Ultimate Played', + properties: { + myProp: 'abc' + } +}) +``` \ No newline at end of file From 7f4cf2b49c9cd26aac7d6bdd711652d9f2b182d6 Mon Sep 17 00:00:00 2001 From: pwseg Date: Thu, 12 Jan 2023 23:22:45 -0600 Subject: [PATCH 19/74] Update Amplitude Classic Destination DOC-629 --- src/_data/catalog/destinations.yml | 275 +++++++++--------- .../destinations/catalog/amplitude/index.md | 78 +++-- 2 files changed, 175 insertions(+), 178 deletions(-) diff --git a/src/_data/catalog/destinations.yml b/src/_data/catalog/destinations.yml index d034fabf70..e65259038e 100644 --- a/src/_data/catalog/destinations.yml +++ b/src/_data/catalog/destinations.yml @@ -1,5 +1,5 @@ # AUTOGENERATED FROM PUBLIC API. DO NOT EDIT -# destination data last updated 2023-01-12 +# destination data last updated 2023-01-12 items: - id: 637e8d185e2dec264895ea89 display_name: 1Flow @@ -574,10 +574,10 @@ items: type: number defaultValue: 0 description: >- - *You must enable setDelay first!* + *You must enable setDelay first!* - Set the initial delay time in seconds with the setting `setDelay` + Set the initial delay time in seconds with the setting `setDelay` enabled. The maximum delay start time of the adjust SDK is 10 seconds. required: false label: delayTime @@ -804,7 +804,7 @@ items: If you have a nested object, separate the name with a `.` For example if - you wanted to map the page referrer, you would put: page.referrer. + you wanted to map the page referrer, you would put: page.referrer. **NOTE**: By default Segment send alls your `properties` as Context Data @@ -917,7 +917,7 @@ items: description: |- Configure merchandising event, such as purchase or currency events. - This is currently in Beta Testing and not generally available. + This is currently in Beta Testing and not generally available. required: true label: 'Merchandising Events ' - name: pageNameFallbackToScreen @@ -965,7 +965,7 @@ items: description: >- Note: This setting is for Server-Side only, and only applies when the Drop Visitor ID setting is disabled and you send a marketingCloudId in the - Adobe Analytics integration object. + Adobe Analytics integration object. ​​ @@ -1312,7 +1312,7 @@ items: description: >- By default, notifications are sent to the Adobe Target backend for incrementing impression count. If false, notifications are not sent for - incrementing impression count. + incrementing impression count. placeholder: '' defaultValue: true required: false @@ -2669,7 +2669,7 @@ items: Mobile Only. If a user has granted your app location permissions, enable this setting so that the SDK will also grab the location of the user. Amplitude will never prompt the user for location permission, so this must - be done by your app. + be done by your app. required: false label: Enable Location Listening - name: endpoint @@ -2677,7 +2677,7 @@ items: defaultValue: '' description: >- Cloud-mode Only (will not work in device-mode). Choose the endpoint - corresponding to your region. + corresponding to your region. EU endpoints aren't supported for device mode. required: true label: Endpoint - name: eventUploadPeriodMillis @@ -2859,7 +2859,7 @@ items: defaultValue: true description: >- Client Side Only - Enabling this will send referrer information as a user - property to Amplitude when you call Segment's `page` method. + property to Amplitude when you call Segment's `page` method. required: false label: Track Referrer to Amplitude - name: trackRevenuePerProduct @@ -2879,7 +2879,7 @@ items: description: >- (Optional) This enables the sending of start and end session events for mobile products. Amplitude's libraries track sessions automatically and - this option is not necessary for session tracking. + this option is not necessary for session tracking. required: false label: Track Session Events to Amplitude - name: trackUtmProperties @@ -2899,7 +2899,7 @@ items: defaultValue: [] description: >- Server-Side and Mobile Only. Configure values to be appended to the user - property array via identify.traits. + property array via identify.traits. required: false label: Traits to Append - name: traitsToIncrement @@ -2916,7 +2916,7 @@ items: defaultValue: [] description: >- Server-Side and Mobile Only. Configure values to be prepended to the user - property array via identify.traits. + property array via identify.traits. required: false label: Traits to Prepend - name: traitsToSetOnce @@ -2924,7 +2924,7 @@ items: defaultValue: [] description: >- Server-Side and Mobile Only. Configure values to be set only once via - identify.traits. + identify.traits. required: false label: Traits to Set Once - name: unsetParamsReferrerOnNewSession @@ -2957,8 +2957,7 @@ items: to not fully respect the "Prefer Anonymous ID for Device ID" setting (Amplitude may set the device ID upon initialization before it gets set to the proper Anonymous ID) if using Analytics.js 1.0. Consider [updating to - Analytics.js 2.0] - (https://segment.com/docs/connections/sources/catalog/libraries/website/javascript/upgrade-to-ajs2/) + Analytics.js 2.0](https://segment.com/docs/connections/sources/catalog/libraries/website/javascript/upgrade-to-ajs2/). required: false label: Use Amplitude Referral - name: useCustomAmplitudeProperties @@ -6732,7 +6731,7 @@ items: when sent to AppsFlyer API. To prevent these event failures in this scenario enable this send the IDFV instead. When the "Can Omit AppsFlyerId" setting is enabled if the IDFA is zeroed out, we will also - send an IDFV when this setting is enabled. + send an IDFV when this setting is enabled. required: false label: >- Fallback to send IDFV when advertisingId key not present (Server-Side @@ -8066,7 +8065,7 @@ items: defaultValue: '' description: >- Select where you want Braze to receive, process, and store data from this - destination. + destination. Choose your Appboy Gateway (ie. US 01, US 02, EU 01, etc.). required: true @@ -9350,7 +9349,7 @@ items: at a different path on that server, provide a value for this option that is the absolute path to the file, e.g. /mycustompath/my-worker.js. VERY IMPORTANT: setting a value here limits the scope of push notifications on - your site. For instance, in the above example, because the service + your site. For instance, in the above example, because the service ,worker file is located within the /mycustompath/ directory, appboy.registerAppboyPushMessages MAY ONLY BE CALLED from web pages that start with http://yoursite.com/mycustompath/. @@ -10731,7 +10730,7 @@ items: description: >+ [Chartbeat expects](http://support.chartbeat.com/docs/#titles) the `document.title` ( Segment `page`'s `props.title`) to populate as - *title*. + *title*.
@@ -10741,7 +10740,7 @@ items: This setting respects Segment's legacy behavior of setting the page name and category as *title* for existing users, but defaults new users to the correct behavior of sending `document.title` as *title* to Chartbeat, and - allows current users to opt-in to the correct behavior if they chose. + allows current users to opt-in to the correct behavior if they chose. required: true label: sendNameAndCategoryAsTitle @@ -11863,7 +11862,7 @@ items: defaultValue: '' description: >- (Web/A.js Only) Add your Comscore Keyword value. This will be added to the - query string as `comscorekw={value}`. + query string as `comscorekw={value}`. required: false label: Comscore Keyword - name: consentFlag @@ -12421,7 +12420,7 @@ items: type: DATETIME description: >- Event timestamp. Optional. Date format is ISO 8601 standard. If empty, - the request upload time will be used. + the request upload time will be used. placeholder: '' defaultValue: '@path': $.timestamp @@ -13109,7 +13108,7 @@ items: Input your event name (case sensitive) on the left and choose the event type from the dropdown. If you do not define these mappings we will fall back on the default mappings defined in our - [documentation](https://segment.com/docs/connections/destinations/catalog/criteo/#track). + [documentation](https://segment.com/docs/connections/destinations/catalog/criteo/#track). required: false label: Event Mappings - name: homeUrl @@ -13829,7 +13828,7 @@ items: Destination. It should be 20 or 64 characters long, and look something like this: 91837a6c9e8b49d0ef71. An API Key is required if you're using our server-side or mobile libraries. For more details you can view the - [Customer.io Docs](https://customer.io/docs/en/segment-destination). + [Customer.io Docs](https://customer.io/docs/en/segment-destination). required: true label: API Key - name: convertToUnixTime @@ -15193,7 +15192,7 @@ items: defaultValue: false description: >- Enable setting this if you want to use the Transaction Counting Method - instead of Items Sold method when assigning `qty` value in the iframe. + instead of Items Sold method when assigning `qty` value in the iframe. required: true label: Use Transaction Counting (Client Side Only) actions: [] @@ -15467,7 +15466,7 @@ items: description: >- Please input the Segment event names on the left and their corresponding Eloqua Custom Object names on the right. This mapping is required for all - events you would like in Eloqua as Custom Objects. + events you would like in Eloqua as Custom Objects. **Note:** If you have set up Custom Object Fields in Eloqua, please ensure @@ -15484,13 +15483,13 @@ items: description: >- Please input the Segment trait names on the left and their corresponding Eloqua Custom Account Field names on the right. The traits must be set up - in the Eloqua dashboard prior to instantiating this mapping. + in the Eloqua dashboard prior to instantiating this mapping. **Note:** If you have set up Custom Account Fields in Eloqua, please ensure the corresponding Segment payload property values are of the same data type specified in Eloqua's dashboard. Eloqua will reject any event - containing a Custom Account Field with an incorrect data type. + containing a Custom Account Field with an incorrect data type. required: false label: Map Custom Traits to Accounts - name: mappedIdentifyTraits @@ -15499,13 +15498,13 @@ items: description: >- Please input the Segment trait names on the left and their corresponding Eloqua Custom Contact Field names on the right. The traits must be set up - in the Eloqua dashboard prior to instantiating this mapping. + in the Eloqua dashboard prior to instantiating this mapping. **Note:** If you have set up Custom Contact Fields in Eloqua, please ensure the corresponding Segment payload property values are of the same data type specified in Eloqua's dashboard. Eloqua will reject any event - containing a Custom Contact Field with an incorrect data type. + containing a Custom Contact Field with an incorrect data type. required: false label: Map Custom Traits to Contacts - name: password @@ -16433,7 +16432,7 @@ items: events will fail when sent to Facebook App Events API. To prevent these event failures in this scenario enable this setting to default to set the IDFA to be zero'd out (ie. '00000000-0000-0000-0000-000000000000') when - sent to Facebook App Events. + sent to Facebook App Events. required: false label: >- Fallback to Zeroed IDFA when advertisingId key not present (Server-Side @@ -18507,7 +18506,7 @@ items: description: >- For pre-purchase events such as `Product Viewed`, `Product Added`, and `Product List Viewed`, choose which Segment property you would like to map - to Facebook's `value` property. + to Facebook's `value` property. required: false label: Value Field Identifier actions: [] @@ -18686,7 +18685,7 @@ items: description: >- For pre-purchase events such as `Product Viewed` and `Product Added`, choose which Segment property you would like to map to Facebook's value - property. + property. required: true label: Value Field Identifier - name: whitelistPiiProperties @@ -22773,7 +22772,7 @@ items: events will fail when sent to Google Adwords. To prevent these event failures in this scenario enable this setting to set the IDFA to be zero'd out (ie. '00000000-0000-0000-0000-000000000000') when sent to Google - Adwords. + Adwords. required: false label: >- Fallback to Zeroed IDFA when advertisingId key not present (Server-Side @@ -22836,7 +22835,7 @@ items: defaultValue: '' description: >- Enter your GOOGLE-CONVERSION-ID. You can get this value from your global - site tag snippet. It should look something like `AW-901243031` + site tag snippet. It should look something like `AW-901243031` required: true label: Google Conversion ID - name: clickConversions @@ -23617,7 +23616,7 @@ items: Ads account. On the right-hand side, map the Segment field that contains the corresponding value See [Google’s documentation on how to create custom conversion - variables.](https://developers.google.com/google-ads/api/docs/conversions/conversion-custom-variables) + variables.](https://developers.google.com/google-ads/api/docs/conversions/conversion-custom-variables) placeholder: '' required: false multiple: false @@ -24295,7 +24294,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -24516,7 +24515,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -24687,7 +24686,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -24874,7 +24873,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -25115,7 +25114,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -25286,7 +25285,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -25485,7 +25484,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -25656,7 +25655,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -25827,7 +25826,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -25951,7 +25950,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -26196,7 +26195,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -26405,7 +26404,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -26578,7 +26577,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -26698,7 +26697,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -26843,7 +26842,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -27014,7 +27013,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -27136,7 +27135,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -27301,7 +27300,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -27439,7 +27438,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -27814,15 +27813,15 @@ items: type: OBJECT description: |2- - The fields to write to the spreadsheet. + The fields to write to the spreadsheet. + + On the left-hand side, input the name of the field as it will appear in the Google Sheet. - On the left-hand side, input the name of the field as it will appear in the Google Sheet. - On the right-hand side, select the field from your data model that maps to the given field in your sheet. - + --- - - + + placeholder: '' required: true multiple: false @@ -28086,7 +28085,7 @@ items: dimensions and metrics. Unlike the client side integration which has the luxury of browsers and the global window `ga` tracker, for server side we will check your `traits` and your settings for custom dimension/metric - mappings and send it with an explicit event. + mappings and send it with an explicit event. required: false label: Enable Server Side Identify - name: enhancedEcommerce @@ -28185,7 +28184,7 @@ items: additional Google Analytics trackers and want to ensure that your Segment tracker has a distinct name. If this is enabled you must prepend this tracker name to any native Google Analytics (except for create) that you - call, e.g. 'segmentGATracker.require(....)' + call, e.g. 'segmentGATracker.require(....)' required: false label: Name Tracker - name: nonInteraction @@ -32597,7 +32596,7 @@ items: description: >- The field from your Segment events that corresponds to your Kable customer's Client ID (by default, Kable uses Segment's userId). For nested - JSON fields, use dot notation. + JSON fields, use dot notation. required: true label: Client ID Field - name: kableClientId @@ -32966,7 +32965,7 @@ items: description: >- Enable this to use the Keen's data enrichment feature for parsing URLs into its components for easier filtering. Note that `userAgent` is only - collected on Android, not on iOS. + collected on Android, not on iOS. required: false label: URL Parsing Addon - name: writeKey @@ -33257,7 +33256,7 @@ items: being created inside of Klaviyo. When enabled, we will never set $id field to your `userId` when you call `.identify()` or `.track()`. Instead, we will only set $email as the primary identifier with your `traits.email` or - `properties.email`. + `properties.email`. required: false label: Enforce Email as Primary Identifier - name: listId @@ -33285,7 +33284,7 @@ items: available when sending server side events. Note that this option may result in superfluous user profiles in Klaviyo and is generally not recommend. If this option is unchecked, we will only accept server side - events that includes the `userId`. + events that includes the `userId`. required: false label: Fallback on Anonymous ID - name: useSegmentSpec @@ -38229,7 +38228,7 @@ items: defaultValue: false description: >- We used to add $ to mixpanel traits as super properties. Enable this if - you would like to use the legacy behavior. + you would like to use the legacy behavior. required: false label: Legacy Super Properties - name: people @@ -39623,7 +39622,7 @@ items: description: >- Segment will map our `asset_id` property to Nielsen's `assetId` field. If you would like to set up a custom property mapping for ad asset ids please - indicate the name of the property here. + indicate the name of the property here. required: false label: Custom Ad Asset Id Property Name - name: appId @@ -39650,7 +39649,7 @@ items: description: >- Segment will map our `asset_id` property to Nielsen's `assetId` field. If you would like to set up a custom property mapping for content asset ids - please indicate the name of the property here. + please indicate the name of the property here. required: false label: Custom Content Asset Id Property Name - name: contentLengthPropertyName @@ -39659,7 +39658,7 @@ items: description: >- Segment will map our `total_length` property to Nielsen's `length` field by default. If you would like to set up a custom property mapping please - indicate the name of the property here. + indicate the name of the property here. required: false label: Content Length Property Name - name: customSectionProperty @@ -39668,7 +39667,7 @@ items: description: >- Segment will map the page/screen `name` field to Nielsen's `section` field. If you would like to set up a custom property mapping for the - page/screen section name please indicate the name of the property here. + page/screen section name please indicate the name of the property here. required: false label: Custom Page/Screen Section Property Name - name: instanceName @@ -39696,7 +39695,7 @@ items: For livestream video positions please enable this setting if you want Segment to default to sending the current time in seconds. If you would like Segment to calculate an offset position value based of - `properties.position` do not enable this setting. + `properties.position` do not enable this setting. required: false label: Enable Default to Current Time for Livestream Playhead Position - name: sfCode @@ -40177,7 +40176,7 @@ items: Viewed" track event will be triggered each time you access an Optimizely live variable. If you're regularly accessing live variables and want to reduce the number of track events triggered, pass the "false" flag, for - example our Android library would be: + example our Android library would be: ``` @@ -40185,7 +40184,7 @@ items: Boolean myVariable = optimizelyClient.getVariableBoolean("myVariable", userId, false); - ``` + ``` And for our iOS library: @@ -40318,7 +40317,7 @@ items: Map here the properties from your event that want to pass to Optimizely as part of the Campaign. The keys for the map correspond to Segment's properties, and the values the Campaign properties. For example, `source - -> campaign_name`. + -> campaign_name`. required: false label: Custom Campaign Properties - name: customExperimentProperties @@ -40328,7 +40327,7 @@ items: Map here the properties from your event that want to pass to Optimizely as part of the Experiment. The keys for the map correspond to Segment's properties, and the values the Experiment properties. For example, `color - -> experiment_color`. + -> experiment_color`. required: false label: Custom Experiment Properties - name: listen @@ -40586,7 +40585,7 @@ items: description: >- You can find your Account ID (`piAId`) under **Marketing > Campaigns** in your [Pardot account](https://pi.pardot.com/campaign). After selecting - your desired website campaign, press **View Tracking Code**. + your desired website campaign, press **View Tracking Code**. required: true label: Account ID - name: businessUnitID @@ -40628,7 +40627,7 @@ items: label: Email Address type: STRING description: |- - The prospect's email address. + The prospect's email address. Used to upsert a prospect in Pardot. If multiple prospects have the given email, the prospect with the latest activity is updated. If there's no prospect with the given email, a prospect is created. Please note that Pardot treats email address as case sensitive and will create multiple prospects for casing differences. placeholder: '' defaultValue: @@ -40876,7 +40875,7 @@ items: description: >- If true, the request’s search includes deleted records. This property only affects [AMPSEA - accounts](https://help.salesforce.com/s/articleView?id=sf.pardot_admin_ampsea_parent.htm&type=5). + accounts](https://help.salesforce.com/s/articleView?id=sf.pardot_admin_ampsea_parent.htm&type=5). If all records with a matching email address are deleted, the one with the latest activity is undeleted and updated. Otherwise, a new prospect is created. placeholder: '' defaultValue: true @@ -40892,7 +40891,7 @@ items: type: OBJECT description: |2- - Additional prospect fields to send to Pardot. + Additional prospect fields to send to Pardot. Only editable fields are accepted. Please see [Pardot docs](https://developer.salesforce.com/docs/marketing/pardot/guide/prospect-v5.html#fields) for more details. On the left-hand side, input the Pardot field name. On the right-hand side, map the Segment field that contains the value. placeholder: '' required: false @@ -43094,7 +43093,7 @@ items: Please enter the trait you would like to be mapped to `EMAIL_PEERMISSION_STATUS_`. This will allow you to track users who opt in and out of marketing communications in your apps and websites. The value - of this trait MUST be a boolean. + of this trait MUST be a boolean. required: false label: Email Permission Status Trait Mapping - name: optOutMobileTrait @@ -43104,7 +43103,7 @@ items: Please enter the trait youw would like to be mapped to MOBILE_PERMISSION_STATUS_. This will allow you to track users who opt in and out of marketing communications in your apps and websites. The value - of this trait MUST be a boolean. + of this trait MUST be a boolean. required: false label: Mobile Permission Status Trait Mapping - name: password @@ -44332,7 +44331,7 @@ items: description: >- Salescamp only track events that mention in below list. Salescamp recommend you to add event in below list that important for your sales - process (ex. "Demo Request Placed", "Order Placed" etc.) + process (ex. "Demo Request Placed", "Order Placed" etc.) required: true label: 'Events ' actions: [] @@ -44617,13 +44616,13 @@ items: The fields used to find Salesforce records for updates. **This is required if the operation is Update or Upsert.** - Any field can function as a matcher, including Record ID, External IDs, standard fields and custom fields. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. - + Any field can function as a matcher, including Record ID, External IDs, standard fields and custom fields. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. + If multiple records are found, no updates will be made. **Please use fields that result in unique records.** - + --- - + placeholder: '' required: false multiple: false @@ -44679,10 +44678,10 @@ items: Additional fields to send to Salesforce. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. This can include standard or custom fields. Custom fields must be predefined in your Salesforce account and the API field name should have __c appended. - + --- - - + + placeholder: '' required: true multiple: false @@ -44765,13 +44764,13 @@ items: The fields used to find Salesforce records for updates. **This is required if the operation is Update or Upsert.** - Any field can function as a matcher, including Record ID, External IDs, standard fields and custom fields. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. - + Any field can function as a matcher, including Record ID, External IDs, standard fields and custom fields. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. + If multiple records are found, no updates will be made. **Please use fields that result in unique records.** - + --- - + placeholder: '' required: false multiple: false @@ -44989,10 +44988,10 @@ items: Additional fields to send to Salesforce. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. This can include standard or custom fields. Custom fields must be predefined in your Salesforce account and the API field name should have __c appended. - + --- - - + + placeholder: '' required: false multiple: false @@ -45075,13 +45074,13 @@ items: The fields used to find Salesforce records for updates. **This is required if the operation is Update or Upsert.** - Any field can function as a matcher, including Record ID, External IDs, standard fields and custom fields. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. - + Any field can function as a matcher, including Record ID, External IDs, standard fields and custom fields. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. + If multiple records are found, no updates will be made. **Please use fields that result in unique records.** - + --- - + placeholder: '' required: false multiple: false @@ -45188,10 +45187,10 @@ items: Additional fields to send to Salesforce. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. This can include standard or custom fields. Custom fields must be predefined in your Salesforce account and the API field name should have __c appended. - + --- - - + + placeholder: '' required: false multiple: false @@ -45274,13 +45273,13 @@ items: The fields used to find Salesforce records for updates. **This is required if the operation is Update or Upsert.** - Any field can function as a matcher, including Record ID, External IDs, standard fields and custom fields. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. - + Any field can function as a matcher, including Record ID, External IDs, standard fields and custom fields. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. + If multiple records are found, no updates will be made. **Please use fields that result in unique records.** - + --- - + placeholder: '' required: false multiple: false @@ -45591,10 +45590,10 @@ items: Additional fields to send to Salesforce. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. This can include standard or custom fields. Custom fields must be predefined in your Salesforce account and the API field name should have __c appended. - + --- - - + + placeholder: '' required: false multiple: false @@ -45677,13 +45676,13 @@ items: The fields used to find Salesforce records for updates. **This is required if the operation is Update or Upsert.** - Any field can function as a matcher, including Record ID, External IDs, standard fields and custom fields. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. - + Any field can function as a matcher, including Record ID, External IDs, standard fields and custom fields. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. + If multiple records are found, no updates will be made. **Please use fields that result in unique records.** - + --- - + placeholder: '' required: false multiple: false @@ -45904,10 +45903,10 @@ items: Additional fields to send to Salesforce. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. This can include standard or custom fields. Custom fields must be predefined in your Salesforce account and the API field name should have __c appended. - + --- - - + + placeholder: '' required: false multiple: false @@ -45990,13 +45989,13 @@ items: The fields used to find Salesforce records for updates. **This is required if the operation is Update or Upsert.** - Any field can function as a matcher, including Record ID, External IDs, standard fields and custom fields. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. - + Any field can function as a matcher, including Record ID, External IDs, standard fields and custom fields. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. + If multiple records are found, no updates will be made. **Please use fields that result in unique records.** - + --- - + placeholder: '' required: false multiple: false @@ -46049,10 +46048,10 @@ items: Additional fields to send to Salesforce. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. This can include standard or custom fields. Custom fields must be predefined in your Salesforce account and the API field name should have __c appended. - + --- - - + + placeholder: '' required: false multiple: false @@ -46314,7 +46313,7 @@ items: type: STRING description: >- The unique key for an event definition in Salesforce Marketing Cloud. - The event defintion must be predefined in SFMC. + The event defintion must be predefined in SFMC. placeholder: '' required: true multiple: false @@ -46912,7 +46911,7 @@ items: type: string defaultValue: '' description: >- - Enter URL for the AWS S3 bucket name or Azure Blob Storage container. + Enter URL for the AWS S3 bucket name or Azure Blob Storage container. (e.g. 'https://my-storage.blob.core.windows.net/my-container' ). required: true label: BUCKET CONTAINER URL @@ -48866,7 +48865,7 @@ items: Reference: [Get All field definitions](https://docs.sendgrid.com/api-reference/custom-fields/get-all-field-definitions) --- - + placeholder: '' required: false multiple: false @@ -48986,7 +48985,7 @@ items: your code versus other code. By default, Sentry displays all frames as expanded. Once you add values to this setting, all frames excepts for those you've added here will appear as collapsed in Sentry. You can still - manually expand all frames from a stack trace in your Sentry dashboard. + manually expand all frames from a stack trace in your Sentry dashboard. It would be suggested to add the current page url, and the host for your CDN here. required: false @@ -49596,7 +49595,7 @@ items: defaultValue: '' description: >- Use "handlebarsjs" templating to format messages sent to Slack. The - default template is "Identified {{name}}. + default template is "Identified {{name}}. {{traits}}". You do not need to append ‘traits’, as this template is specific to `.identify()` calls only and thus is assumed. Make sure @@ -55224,7 +55223,7 @@ items: description: >- If you have any product properties that you would like to map to item-scoped custom attributes inside `site_event_items` for your ecommerce - events, please specify them here. + events, please specify them here. required: false label: Custom Item Attributes actions: [] @@ -55398,7 +55397,7 @@ items: Twitter supports having a single universal website tag for conversions. Please enter your Universal Website Tag Pixel ID. This setting is also required in order to use [Advanced Conversion - Tracking](https://business.twitter.com/en/help/campaign-measurement-and-analytics/conversion-tracking-for-websites.html). + Tracking](https://business.twitter.com/en/help/campaign-measurement-and-analytics/conversion-tracking-for-websites.html). required: false label: Universal Website Tag Pixel ID actions: [] @@ -56631,7 +56630,7 @@ items: defaultValue: '' description: >- WalkMe environment that should be loaded (e.g. Test, Production), you can - find it in the Editor's Snippet area + find it in the Editor's Snippet area required: true label: Environment - name: integrityHash @@ -56657,7 +56656,7 @@ items: defaultValue: '' description: >- WalkMe system ID - you can find it in the Editor's Snippet area (GUID - parameter) + parameter) required: true label: WalkMe system ID actions: [] @@ -57970,7 +57969,7 @@ items: no `userId` is present, the integration will only create or update an organization on `.group()`. To do so, your Zendesk authorization must be configured as an "Agent". **NOTE:** If you would like to link users to - organizations on group calls, do not enable this setting. + organizations on group calls, do not enable this setting. required: false label: Send Group Calls Without UserId - name: events @@ -57992,7 +57991,7 @@ items: description: >- Enable this setting if you would like to remove users organization memberships on `.identify()` calls when you pass a `company.id` trait and - the `company.remove` trait is set to true. + the `company.remove` trait is set to true. required: false label: Enable Removing Users from Organizations - name: subdomain diff --git a/src/connections/destinations/catalog/amplitude/index.md b/src/connections/destinations/catalog/amplitude/index.md index 0fd8c38b69..22325dbab1 100644 --- a/src/connections/destinations/catalog/amplitude/index.md +++ b/src/connections/destinations/catalog/amplitude/index.md @@ -4,10 +4,7 @@ hide-cmodes: true maintenance: true id: 54521fd525e721e32a72ee91 --- -[Amplitude](https://amplitude.com/) is an event tracking and segmentation -platform for your web and mobile apps. By analyzing the actions your users -perform, you can gain a better understanding to drive retention, engagement, -and conversion. +[Amplitude](https://amplitude.com/){:target="_blank"} is an event tracking and segmentation platform for your web and mobile apps. By analyzing the actions your users perform, you can gain a better understanding to drive retention, engagement, and conversion. Segment's Amplitude destination code is open source and available on GitHub. You can view these repositories: - [Android](https://github.com/segment-integrations/analytics-android-integration-amplitude){:target="_blank"} @@ -16,7 +13,7 @@ Segment's Amplitude destination code is open source and available on GitHub. You - [Kotlin](https://github.com/segment-integrations/analytics-kotlin-amplitude){:target="_blank"} - [Swift](https://github.com/segment-integrations/analytics-swift-amplitude){:target="_blank"} -In addition to the docs below, Amplitude created a [integration guide](https://developers.amplitude.com/docs/segment-amplitude-integration). +In addition to Segment's Amplitude documentation, Amplitude provides a [Segment integration guide](https://developers.amplitude.com/docs/segment-amplitude-integration){:target="_blank"}, as well. > note "" > To delete users based on GDPR regulations, you must include a secret key in the **Secret Key** setting of every Amplitude destination. You can find your Secret Key on the [General Settings](https://help.amplitude.com/hc/en-us/articles/235649848-Settings#general) of your Amplitude project. @@ -27,7 +24,7 @@ In addition to the docs below, Amplitude created a [integration guide](https://d > success "" -> **Good to know**: This page is about the Amplitude Segment destination, which receives data _from_ Segment. There's also a page about the [Amplitude Engage Segment source](/docs/connections/sources/catalog/cloud-apps/amplitude-cohorts/), which sends data _to_ Segment! +> **Good to know**: This page is about the Amplitude Segment destination, which receives data _from_ Segment. There's also a page about the [Amplitude Engage Segment source](/docs/connections/sources/catalog/cloud-apps/amplitude-cohorts/), which sends data _to_ Segment. @@ -39,7 +36,8 @@ In addition to the docs below, Amplitude created a [integration guide](https://d 3. Choose which sources to connect the destination to. 4. In the destination settings, enter your Amplitude API key. - You can find your Amplitude API key in the [Amplitude project settings](https://analytics.amplitude.com/settings/projects). It is a 32-character string of numbers and letters. Locate the project you want to receive your Segment data, copy that project's API key, and paste it into your Amplitude destination settings in Segment. + You can find your Amplitude API key in the [Amplitude project settings](https://analytics.amplitude.com/settings/projects){:target="_blank"} +. It is a 32-character string of numbers and letters. Locate the project you want to receive your Segment data, copy that project's API key, and paste it into your Amplitude destination settings in Segment. If you included Segment's JavaScript snippet on your page, then Amplitude's SDK loads on your page automatically and you can use Segment's to begin sending events right away. @@ -76,7 +74,8 @@ analytics.screen({ Page and Screen calls have two important properties: a *page name*, such as "Settings", and a *category*, such as "Merchant". How you pass these properties depends on which Segment library you use. Segment determines when to send events to Amplitude based on the settings you enable, and whether the call has a name or category included. -**Important:** If you enable more than one of the following settings, Segment might send multiple events for the same call. +> warning "" +> If you enable more than one of the following settings, Segment might send multiple events for the same call. ### Event type settings for cloud-mode and Analytics.js @@ -121,8 +120,7 @@ The following settings are available on Android for device-mode connections. | Track All Pages | Always | If a `screen` *name* is provided: `Viewed (Name) Screen`. Otherwise `Loaded a Screen` | "Viewed Settings Screen" | | Track All Screens | Always | Loaded a Screen | "Loaded a Screen" | -You can learn more about Page calls from our [Page spec](/docs/connections/spec/page/) -and Screen calls from our [Screen spec](/docs/connections/spec/screen/). +You can learn more about Page calls from the [Page spec](/docs/connections/spec/page/) and Screen calls from the [Screen spec](/docs/connections/spec/screen/). ## Identify @@ -148,14 +146,13 @@ analytics.identify({ }) ``` -When you make an Identify call, Segment uses the `userId` you provide to set the [User Id in Amplitude](https://help.amplitude.com/hc/en-us/articles/206404628-Step-2-Assign-User-IDs-and-Identify-Your-Users), and -sets any `traits` you provide as Amplitude custom `user_properties`. +When you make an Identify call, Segment uses the `userId` you provide to set the [User Id in Amplitude](https://help.amplitude.com/hc/en-us/articles/206404628-Step-2-Assign-User-IDs-and-Identify-Your-Users){:target="_blank"}, and sets any `traits` you provide as Amplitude custom `user_properties`. ### Merging users with Anonymous ID and User ID To have Amplitude recognize an anonymous user and a known or logged-in user, make sure you include both the user's `userId` and the `anonymousId` they had before that in your Identify call. If you don't include the `anonymousId`, Amplitude can't tell that the anonymous user is the same person as the logged-in user. -If you're using a Segment server library or the Segment HTTP API, you must explicitly include both `anonymousId` and `userId`. If you're using Analytics.js in device-mode, or a bundled SDK, Segment automatically includes `anonymousId` for you. +If you're using a Segment server library or the [Segment HTTP API](/docs/connections/sources/catalog/libraries/server/http-api/), you must explicitly include both `anonymousId` and `userId`. If you're using Analytics.js in device-mode, or a bundled SDK, Segment automatically includes `anonymousId` for you. ### Amplitude Device ID @@ -228,7 +225,7 @@ When a user logs in, be sure to send the same Amplitude `deviceID` in your Ident ## Track -If you're not familiar with the Segment Specs, take a look to understand what the [Track method](/docs/connections/spec/track/) does. Amplitude supports several special properties, all of which are included in the example below: +If you're not familiar with the Segment Specs, take a look to understand what the [Track method](/docs/connections/spec/track/) does. Amplitude supports several special properties, all of which are included in the following example: ```js // On server-side @@ -293,7 +290,9 @@ Segment's iOS and Android sources can send revenue using Amplitude's preferred ` Property names should be `camelCase` for Android implementations, and `snake_case` for iOS implementations. -**Note**: Amplitude does not currently support currency conversion. You should normalize all revenue data to your currency of choice before sending it to Amplitude. +> info "" +> Amplitude [doesn't support currency conversion](https://help.amplitude.com/hc/en-us/articles/115003116888-Track-revenue){:target="_blank"} +. Normalize all revenue data to your currency of choice before sending it to Amplitude. ### Revenue @@ -312,10 +311,10 @@ For Segment's Analytics.js (device-mode), iOS, and Android sources, if you do no | `eventProperties` | Any remaining properties (cloud-mode only) | A NSDictionary or Map of event properties to include in the revenue event. | -^ In Segment's Analytics.js, iOS and Android sources, if `properties.price` isn't present, Segment falls back to `revenue` and sends that as `price`. The Segment iOS and Android sources also do an additional fallback to `total`, if `revenue` isn't present either. +In Segment's Analytics.js, iOS and Android sources, if `properties.price` isn't present, Segment falls back to `revenue` and sends that as `price`. The Segment iOS and Android sources also do an additional fallback to `total`, if `revenue` isn't present either. > success "" -> **Tip** If your site allows users to perform a single transaction with multiple products (such as a shopping cart checkout), we recommend that you use an [Order Completed](/docs/connections/destinations/catalog/amplitude/#order-completed) event to track revenue with Amplitude. +> **Tip** If your site allows users to perform a single transaction with multiple products (such as a shopping cart checkout), Segment recommends that you use an [Order Completed](/docs/connections/destinations/catalog/amplitude/#order-completed) event to track revenue with Amplitude. @@ -375,16 +374,16 @@ If you disable the setting ("off"), Segment sends a single revenue event with th If you enable the setting ("on"), Segment sends a single revenue event for each product that was purchased. Revenue data is added to each "Product Purchased" event, and the "Order Completed" event does not contain any native Amplitude revenue data. -Make sure you are using formatting your events using the [Track method spec](/docs/connections/spec/track/), and pass at minimum a `revenue` property, as well as a `price` and `quantity` property for each product in the products list. +Make sure you're formatting your events using the [Track method spec](/docs/connections/spec/track/), and pass at minimum a `revenue` property, as well as a `price` and `quantity` property for each product in the products list. ## Group If you're not familiar with the Segment Specs, take a look to understand what the [Group method](/docs/connections/spec/group/) does. > warning "" -> Groups are an enterprise-only feature in Amplitude, and are only available if you've purchased the Accounts add-on. +> Groups are an enterprise-only feature in Amplitude and are only available if you've purchased the Accounts add-on. -The example below shows a Group call made from a server library. +The following example shows a Group call made from a server library: ```js // On server-side @@ -397,7 +396,7 @@ analytics.group("some_group_id", { }) ``` -And the example below shows a call made from a device-mode library that sends directly from the client. +And this example shows a call made from a device-mode library that sends directly from the client: ```js // On client-side @@ -449,10 +448,9 @@ Segment Alias call allows you to associate a Segment user's `previousId` with the user's `userId`, or what Amplitude refers to, respectively, as a `user_id` and a `global_user_id`. -By default, Segment does **NOT** send Alias events to Amplitude. To forward Alias events from Segment, go to your Amplitude destination settings in the -Segment web app, and set the **Enable Alias** setting to "on". Once enabled, Segment forwards Alias events from Segment's servers only. This means -that Alias events reach Amplitude only when you're sending events from the client and have set your Amplitude instance's connection mode to "Cloud Mode", -or are sending Alias events from a Segment server-side library (such as Node). +By default, Segment does **NOT** send Alias events to Amplitude. To forward Alias events from Segment, go to your Amplitude destination settings in the Segment web app, and set the **Enable Alias** setting to "on". + +Once enabled, Segment forwards Alias events from Segment's servers only. This means that Alias events reach Amplitude only when you're sending events from the client and have set your Amplitude instance's connection mode to "Cloud Mode", or are sending Alias events from a Segment server-side library (such as Node). > note "" > To use Alias, you must have the Amplitude Portfolio add-on enabled. @@ -466,10 +464,10 @@ For more information, see the [Segment Spec page for the Alias method](/docs/con ### Mapping Users -Mapping a Segment user's `previousId` to the user's `userId` in Amplitude is as -simple as invoking a Segment Alias method with an argument for each value. +You can map a Segment user's `previousId` to the user's `userId` in Amplitude by invoking a Segment Alias method with an argument for each value. + The example Alias call below maps the `previousId` with the value of `123` to the `userId` with a value of `456` in Amplitude. Both user `123` and `456` still have separate user profiles, but the profiles get merged together when you look at the user's behavior in -[Amplitude's Cross Project view](https://help.amplitude.com/hc/en-us/articles/360002750712-Portfolio-Cross-Project-Analysis#user-mapping-aliasing). +[Amplitude's Cross Project view](https://help.amplitude.com/hc/en-us/articles/360002750712-Portfolio-Cross-Project-Analysis#user-mapping-aliasing){:target="_blank"}. This kind of mapping is useful for users who have different ids across different Amplitude projects. The user's `user_ids` act as child ids, and can all be mapped to a single `global_user_id` in Amplitude. This allows you to analyze the user's aggregate behavior in Amplitude's Cross Portfolio view. @@ -513,7 +511,7 @@ analytics.alias({ [Segment doesn't have a concept for a session](https://segment.com/blog/facts-vs-stories-why-segment-has-no-sessions-api/). -Device-mode calls to Amplitude include session information because Segment bundles Amplitude's SDK. To set up the same `sessionId` for cloud-mode calls to Amplitude, you must explicitly set the [`session_id`](https://developers.amplitude.com/docs/http-api-v2#optional-keyst) as an integration-specific option, as in the example below. +Device-mode calls to Amplitude include session information because Segment bundles Amplitude's SDK. To set up the same `sessionId` for cloud-mode calls to Amplitude, you must explicitly set the [`session_id`](https://developers.amplitude.com/docs/http-api-v2#optional-keyst){:target="_blank"} as an integration-specific option, as in the example below. ```js { @@ -537,7 +535,7 @@ Device-mode calls to Amplitude include session information because Segment bundl You must pass the start time of a session as ``. -When you pass a timestamp value from the `session_id` it must be in Unix format or else it generates an error when it is delivered to Amplitude. For example, a date of January 1, 2020 and 9:30am UTC would be written as `2020-12-07T19:33:44+00:00` in ISO 8601, but `1577871000` in Unix epoch time. There are many tools and libraries available to help you convert your timestamps. +When you pass a timestamp value from the `session_id` it must be in Unix format, otherwise it generates an error when it is delivered to Amplitude. For example, a date of January 1, 2020 and 9:30am UTC would be written as `2020-12-07T19:33:44+00:00` in ISO 8601, but `1577871000` in Unix epoch time. There are many tools and libraries available to help you convert your timestamps. ### Setting event-level groups using Track calls @@ -564,7 +562,7 @@ analytics.track("Clicked Benefits Dropdown", { ### Setting Amplitude Version User Property using Identify calls -If you are sending event data to Amplitude in cloud-mode (through the Segment servers) and want to use the [Amplitude Release objects feature](https://help.amplitude.com/hc/en-us/articles/360017800371), you can set the app version user property as in the example below. Be sure that you send the version details in the context object and not as a standard user trait. +If you are sending event data to Amplitude in cloud-mode (through the Segment servers) and want to use the [Amplitude Release objects feature](https://help.amplitude.com/hc/en-us/articles/360017800371){:target="_blank"}, you can set the app version user property as in the example below. Make sure to send the version details in the `context` object and not as a standard user trait. ```js analytics.identify('testUser', { @@ -613,13 +611,13 @@ Amplitude does not prompt the user for location permission, so your app must exp On iOS, the user's location is only recorded once per session. If you need to force update the location in Amplitude, you can use the native method -`updateLocation` (iOS only) as documented -[here](https://developers.amplitude.com/docs/ios). When you call `enableLocationListening` on the iOS SDK, it forces the SDK to update (and overwrite) the initial location that was cached during app startup. +`updateLocation` (iOS only) referenced in [Amplitude's iOS SDK documentation](https://developers.amplitude.com/docs/ios){:target="_blank"} +. When you call `enableLocationListening` on the iOS SDK, it forces the SDK to update (and overwrite) the initial location that was cached during app startup. On Android, when enabled, this setting adds a latitude and longitude property to each Track call, which reflecte where geographically the event was triggered. -Even you disable location listening, Amplitude's ingestion layer attempts to determine the user's location from their IP address. To prevent tracking of any location information, contact your Amplitude CSM to disable all location tracking. +Even if you disable location listening, Amplitude's ingestion layer attempts to determine the user's location from their IP address. To prevent tracking of any location information, contact your Amplitude CSM to disable all location tracking. ### Set AdvertisingId for DeviceId @@ -662,7 +660,7 @@ be useful if you are logging events triggered by push notifications, for example. To set an out of session event, send the a Track call with an integration option property `outOfSession` set to `true`. -The example below shows how you might set this on iOS. +The example below shows how you might set this on iOS: ```objc [[SEGAnalytics sharedAnalytics] @@ -678,7 +676,7 @@ The example below shows how you might set this on iOS. ]; ``` -The example below shows how you might set this on Android. +The following example shows how you might set this on Android: ```java Properties properties = new Properties(); @@ -695,7 +693,7 @@ The Segment mobile device-mode bundles for Amplitude map Segment's `flush` metho ### Reset -The Segment mobile device-mode bundles for Amplitude support logging out users in Amplitude using Segment's `reset` method. You do not need to aliasing users, as Amplitude merges user data on the backend so that any events up to that point from the same client are tracked under the same user. +The Segment mobile device-mode bundles for Amplitude support logging out users in Amplitude using Segment's `reset` method. You don't need to alias users, as Amplitude merges user data on the backend so that any events up to that point from the same client are tracked under the same user. Segment logs the user out by setting the `userId` to `nil` and calling Amplitude's method to regenerate a new `deviceId`. @@ -703,12 +701,12 @@ Segment logs the user out by setting the `userId` to `nil` and calling Amplitude ### Instrumentation Explorer -Amplitude offers a robust [Instrumentation Explorer/Debugger](https://help.amplitude.com/hc/en-us/articles/360003032451-Instrumentation-Explorer-Debugger). This is a helpful Chrome extension that shows each page interaction that sends an event to Amplitude. +Amplitude offers a robust [Instrumentation Explorer/Debugger](https://help.amplitude.com/hc/en-us/articles/360003032451-Instrumentation-Explorer-Debugger){:target="_blank"}. This is a helpful Chrome extension that shows each page interaction that sends an event to Amplitude. ### Amplitude/Segment FAQ -Have a question about the Amplitude/Segment integration that's already been answered? Take a look at [Amplitude's FAQ](https://developers.amplitude.com/docs/segment-amplitude-integration) for common issues integrating Amplitude with Segment. +Have a question about the Amplitude/Segment integration that's already been answered? Take a look at [Amplitude's FAQ](https://developers.amplitude.com/docs/segment-amplitude-integration){:target="_blank"} for common issues integrating Amplitude with Segment. ### I Don't See My Data In Amplitude -If you aren't seeing your data arrive in Amplitude, we recommend you start by taking a look at our [Analytics.js Guide on validating data being transmitted](/docs/connections/sources/catalog/libraries/website/javascript/troubleshooting#is-data-being-transmitted-to-your-third-party-destinations) to your third-party destination. +If you aren't seeing your data arrive in Amplitude, take a look at our Analytics.js [guide to validating data being transmitted](/docs/connections/sources/catalog/libraries/website/javascript/troubleshooting#is-data-being-transmitted-to-your-third-party-destinations) to your third-party destination. From 88652d30c8c37b3377b0143a508ecf5a81dbb74c Mon Sep 17 00:00:00 2001 From: pwseg Date: Fri, 13 Jan 2023 10:15:38 -0600 Subject: [PATCH 20/74] Update EU Endpoint Language DOC-629 --- src/_data/catalog/destinations.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/_data/catalog/destinations.yml b/src/_data/catalog/destinations.yml index e65259038e..e6d359408c 100644 --- a/src/_data/catalog/destinations.yml +++ b/src/_data/catalog/destinations.yml @@ -2676,8 +2676,7 @@ items: type: select defaultValue: '' description: >- - Cloud-mode Only (will not work in device-mode). Choose the endpoint - corresponding to your region. EU endpoints aren't supported for device mode. + Cloud-mode. Choose the endpoint corresponding to your region. EU endpoints aren't supported for device mode. required: true label: Endpoint - name: eventUploadPeriodMillis From 7d8ea57c40ce9a014833b7d4f0a6b838c493fcc6 Mon Sep 17 00:00:00 2001 From: Sarah Rudy <78389005+sarahrudy@users.noreply.github.com> Date: Fri, 13 Jan 2023 10:26:49 -0700 Subject: [PATCH 21/74] Update index.md Add note about the API Key being region specific --- .../destinations/catalog/actions-amplitude/index.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/connections/destinations/catalog/actions-amplitude/index.md b/src/connections/destinations/catalog/actions-amplitude/index.md index 4a48a4f384..ff4d616544 100644 --- a/src/connections/destinations/catalog/actions-amplitude/index.md +++ b/src/connections/destinations/catalog/actions-amplitude/index.md @@ -35,6 +35,9 @@ Amplitude (Actions) provides the following benefits over the classic Amplitude d Once you have a mapping, you can follow the steps in the Destinations Actions documentation on [Customizing mappings](/docs/connections/destinations/actions/#customizing-mappings). +> info "" +> The Amplitude API Key is region-specific. For example, if you have a North America Amplitude account and API key, you would need to make sure the **Endpoint Region** in your Amplitude destination settings is set to North America. + ### Log Purchases in existing destination instances Initially, the Log Event Action was reporting purchases to Amplitude for all events containing a `products` array, even if the products were just added to cart. This inflated the LTV Chart in Amplitude. @@ -226,4 +229,4 @@ In the following example, the Amplitude User property `friendCount` equals 4. ``` js "traits" : {"$add": {"friendCount": 3} } "traits" : {"$add": {"friendCount": 1} } -``` \ No newline at end of file +``` From b1c861b24c66cb6bff0e8f0f6ca6cf32998e754e Mon Sep 17 00:00:00 2001 From: Sarah Rudy <78389005+sarahrudy@users.noreply.github.com> Date: Fri, 13 Jan 2023 11:15:28 -0700 Subject: [PATCH 22/74] Revert commit --- .../destinations/catalog/actions-amplitude/index.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/connections/destinations/catalog/actions-amplitude/index.md b/src/connections/destinations/catalog/actions-amplitude/index.md index ff4d616544..4d594ab5d7 100644 --- a/src/connections/destinations/catalog/actions-amplitude/index.md +++ b/src/connections/destinations/catalog/actions-amplitude/index.md @@ -35,9 +35,6 @@ Amplitude (Actions) provides the following benefits over the classic Amplitude d Once you have a mapping, you can follow the steps in the Destinations Actions documentation on [Customizing mappings](/docs/connections/destinations/actions/#customizing-mappings). -> info "" -> The Amplitude API Key is region-specific. For example, if you have a North America Amplitude account and API key, you would need to make sure the **Endpoint Region** in your Amplitude destination settings is set to North America. - ### Log Purchases in existing destination instances Initially, the Log Event Action was reporting purchases to Amplitude for all events containing a `products` array, even if the products were just added to cart. This inflated the LTV Chart in Amplitude. From 893088f2e8b377a91d0f46c0e26c45d099c69331 Mon Sep 17 00:00:00 2001 From: bobbyatsegment <93934274+bobbyatsegment@users.noreply.github.com> Date: Fri, 13 Jan 2023 11:51:25 -0700 Subject: [PATCH 23/74] Add section highlighting User Data parameter mappings to Facebook Conversions API Destination (#4025) * Update index.md * Update src/connections/destinations/catalog/actions-facebook-conversions-api/index.md Co-authored-by: markzegarelli Co-authored-by: markzegarelli --- .../actions-facebook-conversions-api/index.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/connections/destinations/catalog/actions-facebook-conversions-api/index.md b/src/connections/destinations/catalog/actions-facebook-conversions-api/index.md index 0f69776c35..8d313a9e74 100644 --- a/src/connections/destinations/catalog/actions-facebook-conversions-api/index.md +++ b/src/connections/destinations/catalog/actions-facebook-conversions-api/index.md @@ -149,6 +149,24 @@ Segment creates a SHA-256 hash of the following fields before sending to Faceboo If you use Facebook Pixel, the Pixel library also hashes the External ID. This means External IDs will match across Facebook Pixel and Facebook Conversions API if they use the External ID for [deduplication](https://developers.facebook.com/docs/marketing-api/conversions-api/deduplicate-pixel-and-server-events/#fbp-or-external-id){:target="_blank"}. +### User Data Parameters + +Segment automatically maps User Data fields to their corresponding parameters [as expected by the Conversions API](https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/customer-information-parameters/){:target="_blank"} before sending to Facebook: + +| User Data Field | Conversions API User Data Parameter | +|-----------------|-------------------------------------| +| External ID | external_id | +| Email | em | +| Phone | ph | +| Gender | ge | +| Date of Birth | db | +| Last Name | ln | +| First Name | fn | +| City | ct | +| State | st | +| Zip Code | zp | +| Country | country | + ### Server Event Parameter Requirements Facebook requires the `action_source` server event parameter for all events sent to the Facebook Conversions API. This parameter specifies where the conversions occur. If `action_source` is set to **website**, then the `client_user_agent` and the `event_source_url` parameters are also required. Events sent to the Conversions API that don't meet the requirements may not be available for optimization, targeting, or measurement. From e3527ec6ee3a921ccd15e5d8b566208cf2f59f40 Mon Sep 17 00:00:00 2001 From: stayseesong Date: Fri, 13 Jan 2023 14:16:21 -0800 Subject: [PATCH 24/74] edits --- .../catalog/libraries/server/node/index.md | 20 ++++++++++++++++++- .../libraries/server/node/migration.md | 2 +- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/connections/sources/catalog/libraries/server/node/index.md b/src/connections/sources/catalog/libraries/server/node/index.md index 11515a57b5..38f1a77f5d 100644 --- a/src/connections/sources/catalog/libraries/server/node/index.md +++ b/src/connections/sources/catalog/libraries/server/node/index.md @@ -348,7 +348,7 @@ analytics.on('error', (err) => console.error(err)) ### Event emitter interface -The event emitter interface allows you to track when certain things happen in the app, such as a track call or an error, and it will call the function you provided with some arguments when that event happens. +The event emitter interface allows you to track events, such as `track` and `identify` calls, and it calls the function you provided with some arguments upon successful delivery. `error` emits on delivery error. See the complete list of emitted events in the [GitHub Node repository](https://github.com/segmentio/analytics-next/blob/master/packages/node/src/app/emitter.ts). ```javascript analytics.on('error', (err) => console.error(err)) @@ -358,6 +358,24 @@ analytics.on('identify', (ctx) => console.log(ctx)) analytics.on('track', (ctx) => console.log(ctx)) ``` +Use the emitter to log all HTTP Requests. + + ```javascript + analytics.on('http_request', (event) => console.log(event)) + + // when triggered, emits an event of the shape: + { + url: 'https://api.segment.io/v1/batch', + method: 'POST', + headers: { + 'Content-Type': 'application/json', + ... + }, + body: '...', + } + ``` + + ## Plugin architecture When you develop against [Analytics.js 2.0](/docs/connections/sources/catalog/libraries/website/javascript/), the plugins you write can augment functionality, enrich data, and control the flow and delivery of events. From modifying event payloads to changing analytics functionality, plugins help to speed up the process of getting things done. diff --git a/src/connections/sources/catalog/libraries/server/node/migration.md b/src/connections/sources/catalog/libraries/server/node/migration.md index ac5d055220..b80245bd44 100644 --- a/src/connections/sources/catalog/libraries/server/node/migration.md +++ b/src/connections/sources/catalog/libraries/server/node/migration.md @@ -29,7 +29,7 @@ If you're using the [classic version of Analytics Node.js](/docs/connections/sou After: ```javascript - const analytics = new Analytics({ writeKey: '' }) + const analytics = new Analytics({ writeKey: '' }) ``` 3. Change flushing to [graceful shutdown](/docs/connections/sources/catalog/libraries/server/node//#graceful-shutdown). From 8a1d8d1fdab8df159367756dbc3b47d8181959df Mon Sep 17 00:00:00 2001 From: stayseesong Date: Fri, 13 Jan 2023 14:31:17 -0800 Subject: [PATCH 25/74] added react native quick link --- src/protocols/apis-and-extensions/typewriter.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/protocols/apis-and-extensions/typewriter.md b/src/protocols/apis-and-extensions/typewriter.md index 5011d48bd9..402f052578 100644 --- a/src/protocols/apis-and-extensions/typewriter.md +++ b/src/protocols/apis-and-extensions/typewriter.md @@ -51,9 +51,10 @@ Typewriter also helps teams adopt [analytics best practices](/docs/protocols/tra To get started, check out one of the quickstart guides below: - [Browser Quickstart](#browser-quickstart) +- [Kotlin Quickstart](#kotlin-quickstart) - [Node.js Quickstart](#nodejs-quickstart) +- [React Native Quickstart](#react-native-quickstart) - [Swift Quickstart](#swift-quickstart) -- [Kotlin Quickstart](#kotlin-quickstart) > info "" > For use with the Analytics-iOS and Analytics-Android SDK, use [Typewriter v7](/docs/protocols/apis-and-extensions/typewriter-v7). From 60204ff29b717e8991f581f4c0955a5131db09a4 Mon Sep 17 00:00:00 2001 From: Sal Olivares Date: Wed, 7 Dec 2022 18:26:10 -0800 Subject: [PATCH 26/74] add section for api token security --- src/api/public-api/index.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/api/public-api/index.md b/src/api/public-api/index.md index 7e93d6cb3b..e653b66fee 100644 --- a/src/api/public-api/index.md +++ b/src/api/public-api/index.md @@ -24,4 +24,25 @@ The Public API includes the following benefits over the Config API: | Improved architecture | The Public API is built with improved security, checks for authentication, authorization, input validation, HTTPS exposed services, auto-scaling, and more in mind. | | Cleaner mapping | The Public API uses unique IDs for reference, in place of slugs in the Config API. Unique IDs are, by design, unique. | | Available in Europe | The Public API is accessible to both US and EU-based workspaces. | -| Increased reliability | The Public API features more stable endpoints, and a 99.8% success rate | \ No newline at end of file +| Increased reliability | The Public API features more stable endpoints, and a 99.8% success rate | + +## API Token Security + +To enhance API token security, Segment partners with GitHub to prevent fraudulent use of exposed API tokens found in public git repositories. Malicious actors can use exposed tokens to perform unauthorized actions in your Segment workspace. + +GitHub scans each commit in public repositories for Public API tokens and detected tokens are sent to Segment. Valid tokens are automatically revoked and workspace owners are notified. This process, Github identifying a token and Segment revoking it, typically takes seconds. + +Learn more about [GitHub's secret scanning program](https://docs.github.com/en/developers/overview/secret-scanning-partner-program). + +### Frequently Asked Questions +#### What should I do if I see a notification that my token was exposed? +In most cases, identifying and revoking an exposed token takes seconds. Still, we recommend you check the [audit trail](/docs/segment-app/iam/audit-trail/) to ensure no unauthorized actions were taken with the token. + +#### How did my token get exposed? +Typically, tokens are exposed when developers commit them to a public git repository. This can happen when developers use a token in a local development environment and forget to remove it before committing their code. + +#### Why are exposed tokens automatically revoked? +By automatically revoking the exposed token, we help keep your workspace secure and prevent potential abuse of the token. + +#### How do I enable this feature? +This feature is automatically enabled for all workspaces on Team or Business tier plans. \ No newline at end of file From 992b649589f6d975db1a4dde7ace69185aff2261 Mon Sep 17 00:00:00 2001 From: Sal Olivares Date: Wed, 7 Dec 2022 18:31:14 -0800 Subject: [PATCH 27/74] update copy --- src/api/public-api/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/public-api/index.md b/src/api/public-api/index.md index e653b66fee..be949399d4 100644 --- a/src/api/public-api/index.md +++ b/src/api/public-api/index.md @@ -30,7 +30,7 @@ The Public API includes the following benefits over the Config API: To enhance API token security, Segment partners with GitHub to prevent fraudulent use of exposed API tokens found in public git repositories. Malicious actors can use exposed tokens to perform unauthorized actions in your Segment workspace. -GitHub scans each commit in public repositories for Public API tokens and detected tokens are sent to Segment. Valid tokens are automatically revoked and workspace owners are notified. This process, Github identifying a token and Segment revoking it, typically takes seconds. +GitHub scans each commit in public repositories for Public API tokens and detected tokens are sent to Segment. Valid tokens are automatically revoked and workspace owners are notified. This process, GitHub identifying a token and Segment revoking it, typically takes seconds. Learn more about [GitHub's secret scanning program](https://docs.github.com/en/developers/overview/secret-scanning-partner-program). @@ -42,7 +42,7 @@ In most cases, identifying and revoking an exposed token takes seconds. Still, w Typically, tokens are exposed when developers commit them to a public git repository. This can happen when developers use a token in a local development environment and forget to remove it before committing their code. #### Why are exposed tokens automatically revoked? -By automatically revoking the exposed token, we help keep your workspace secure and prevent potential abuse of the token. +By automatically revoking the exposed token, Segment helps keep your workspace secure and prevents potential abuse of the token. #### How do I enable this feature? This feature is automatically enabled for all workspaces on Team or Business tier plans. \ No newline at end of file From c8cb023bd6bbd759176ff4dbe32deea12a879cf6 Mon Sep 17 00:00:00 2001 From: markzegarelli Date: Thu, 8 Dec 2022 11:28:20 -0800 Subject: [PATCH 28/74] Small edits --- src/api/public-api/index.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/api/public-api/index.md b/src/api/public-api/index.md index be949399d4..c3eda9bba1 100644 --- a/src/api/public-api/index.md +++ b/src/api/public-api/index.md @@ -32,14 +32,14 @@ To enhance API token security, Segment partners with GitHub to prevent fraudulen GitHub scans each commit in public repositories for Public API tokens and detected tokens are sent to Segment. Valid tokens are automatically revoked and workspace owners are notified. This process, GitHub identifying a token and Segment revoking it, typically takes seconds. -Learn more about [GitHub's secret scanning program](https://docs.github.com/en/developers/overview/secret-scanning-partner-program). +Learn more about [GitHub's secret scanning program](https://docs.github.com/en/developers/overview/secret-scanning-partner-program){:target="_blank"}. ### Frequently Asked Questions #### What should I do if I see a notification that my token was exposed? -In most cases, identifying and revoking an exposed token takes seconds. Still, we recommend you check the [audit trail](/docs/segment-app/iam/audit-trail/) to ensure no unauthorized actions were taken with the token. +In most cases, identifying and revoking an exposed token takes seconds. Segment recommends you check the [audit trail](/docs/segment-app/iam/audit-trail/) to ensure no unauthorized actions were taken with the token. #### How did my token get exposed? -Typically, tokens are exposed when developers commit them to a public git repository. This can happen when developers use a token in a local development environment and forget to remove it before committing their code. +Developers can accidentally commit tokens to public repositories, exposing them to the public. This can happen when developers use a token in a local development environment and forget to remove it before committing their code. #### Why are exposed tokens automatically revoked? By automatically revoking the exposed token, Segment helps keep your workspace secure and prevents potential abuse of the token. From 891d57ec15464f3d93cca6c48006c432a3206c2e Mon Sep 17 00:00:00 2001 From: Sal Olivares Date: Fri, 9 Dec 2022 14:00:41 -0800 Subject: [PATCH 29/74] Update src/api/public-api.md Co-authored-by: stayseesong <83784848+stayseesong@users.noreply.github.com> --- src/api/public-api/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/public-api/index.md b/src/api/public-api/index.md index c3eda9bba1..a5f2c9e25f 100644 --- a/src/api/public-api/index.md +++ b/src/api/public-api/index.md @@ -28,7 +28,7 @@ The Public API includes the following benefits over the Config API: ## API Token Security -To enhance API token security, Segment partners with GitHub to prevent fraudulent use of exposed API tokens found in public git repositories. Malicious actors can use exposed tokens to perform unauthorized actions in your Segment workspace. +To enhance API token security, Segment partners with GitHub to prevent fraudulent use of exposed API tokens found in public git repositories. This helps to prevent malicious actors from using exposed tokens to perform unauthorized actions in your Segment workspace. GitHub scans each commit in public repositories for Public API tokens and detected tokens are sent to Segment. Valid tokens are automatically revoked and workspace owners are notified. This process, GitHub identifying a token and Segment revoking it, typically takes seconds. From a61448e89c9aa4f13ee60e8660c23b2358fe5079 Mon Sep 17 00:00:00 2001 From: Sal Olivares Date: Fri, 9 Dec 2022 14:01:09 -0800 Subject: [PATCH 30/74] Update src/api/public-api.md Co-authored-by: stayseesong <83784848+stayseesong@users.noreply.github.com> --- src/api/public-api/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/public-api/index.md b/src/api/public-api/index.md index a5f2c9e25f..34ed7b1d07 100644 --- a/src/api/public-api/index.md +++ b/src/api/public-api/index.md @@ -30,7 +30,7 @@ The Public API includes the following benefits over the Config API: To enhance API token security, Segment partners with GitHub to prevent fraudulent use of exposed API tokens found in public git repositories. This helps to prevent malicious actors from using exposed tokens to perform unauthorized actions in your Segment workspace. -GitHub scans each commit in public repositories for Public API tokens and detected tokens are sent to Segment. Valid tokens are automatically revoked and workspace owners are notified. This process, GitHub identifying a token and Segment revoking it, typically takes seconds. +Within seconds, GitHub scans each commit in public repositories for Public API tokens, and sends detected tokens to Segment. Valid tokens are automatically revoked and workspace owners are notified. Learn more about [GitHub's secret scanning program](https://docs.github.com/en/developers/overview/secret-scanning-partner-program){:target="_blank"}. From 0d069f012db8ab170a969206f1e7b4cdf331b15f Mon Sep 17 00:00:00 2001 From: stayseesong Date: Fri, 13 Jan 2023 14:48:45 -0800 Subject: [PATCH 31/74] [netlify-build] --- .../catalog/libraries/server/node/classic.md | 4 ++-- .../catalog/libraries/server/node/index.md | 15 +++++++-------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/connections/sources/catalog/libraries/server/node/classic.md b/src/connections/sources/catalog/libraries/server/node/classic.md index 13a4f57c1f..d8bb4159e1 100644 --- a/src/connections/sources/catalog/libraries/server/node/classic.md +++ b/src/connections/sources/catalog/libraries/server/node/classic.md @@ -2,11 +2,11 @@ title: Analytics for Node.js Classic repo: analytics-node strat: node-js -hidden: true +hidden: false --- > info "Upgrade to the new version of Analytics Node.js" -> Upgrade to the new version of Analytics Node.js. See the updated [Analytics Node.js docs](/docs/connections/sources/catalog/libraries/server/node) to learn more. +> There's a new version of Analytics Node.js. [Upgrade](/docs/connections/sources/catalog/libraries/server/node/migration/) to the latest version. See the updated [Analytics Node.js docs](/docs/connections/sources/catalog/libraries/server/node) to learn more. Segment's Node.js library lets you record analytics data from your node code. The requests hit Segment's servers, and then Segment routes your data to any destinations you have enabled. diff --git a/src/connections/sources/catalog/libraries/server/node/index.md b/src/connections/sources/catalog/libraries/server/node/index.md index 38f1a77f5d..fd56b77dfc 100644 --- a/src/connections/sources/catalog/libraries/server/node/index.md +++ b/src/connections/sources/catalog/libraries/server/node/index.md @@ -391,13 +391,13 @@ Plugins are bound by Analytics.js 2.0 which handles operations such as observabi Non-critical plugins run through a timeline that executes in order of insertion based on the entry type. Segment has these five entry types of non-critical plugins: -| Type | Details | -| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `before` | Executes before event processing begins. These are plugins that run before any other plugins run.

For example, validating events before passing them along to other plugins. A failure here could halt the event pipeline.

See the example of how Analytics.js uses the [Event Validation plugin](https://github.com/segmentio/analytics-next/blob/master/packages/browser/src/plugins/validation/index.ts){:target="_blank"} to verify that every event has the correct shape. | -| `enrichment` | Executes as the first level of event processing. These plugins modify an event.

See the example of how Analytics.js uses the [Page Enrichment plugin](https://github.com/segmentio/analytics-next/blob/master/packages/browser/src/plugins/page-enrichment/index.ts){:target="_blank"} to enrich every event with page information. | -| `destination` | Executes as events begin to pass off to destinations.

This doesn't modify the event outside of the specific destination, and failure doesn't halt the execution. | -| `after` | Executes after all event processing completes. You can use this to perform cleanup operations.

An example of this is the [Segment.io Plugin](https://github.com/segmentio/analytics-next/blob/master/packages/browser/src/plugins/segmentio/index.ts){:target="_blank"} which waits for destinations to succeed or fail so it can send it observability metrics. | -| `utility` | Executes once during the bootstrap, to give you an outlet to make any modifications as to how Analytics.js works internally. This allows you to augment Analytics.js functionality. | +| Type | Details +------ | -------- +| `before` | Executes before event processing begins. These are plugins that run before any other plugins run.

For example, validating events before passing them along to other plugins. A failure here could halt the event pipeline.

See the example of how Analytics.js uses the [Event Validation plugin](https://github.com/segmentio/analytics-next/blob/master/packages/browser/src/plugins/validation/index.ts){:target="_blank"} to verify that every event has the correct shape. +| `enrichment` | Executes as the first level of event processing. These plugins modify an event.

See the example of how Analytics.js uses the [Page Enrichment plugin](https://github.com/segmentio/analytics-next/blob/master/packages/browser/src/plugins/page-enrichment/index.ts){:target="_blank"} to enrich every event with page information. +| `destination` | Executes as events begin to pass off to destinations.

This doesn't modify the event outside of the specific destination, and failure doesn't halt the execution. +| `after` | Executes after all event processing completes. You can use this to perform cleanup operations.

An example of this is the [Segment.io Plugin](https://github.com/segmentio/analytics-next/blob/master/packages/browser/src/plugins/segmentio/index.ts){:target="_blank"} which waits for destinations to succeed or fail so it can send it observability metrics. +| `utility` | Executes once during the bootstrap, to give you an outlet to make any modifications as to how Analytics.js works internally. This allows you to augment Analytics.js functionality. ### Example plugins Here's an example of a plugin that converts all track event names to lowercase before the event goes through the rest of the pipeline: @@ -455,7 +455,6 @@ const identityStitching = () => { return identity } - ``` You can view Segment's [existing plugins](https://github.com/segmentio/analytics-next/tree/master/src/plugins){:target="_blank"} to see more examples. From ae1020c6aac35bff1e1874916b7a5b507edbe9a5 Mon Sep 17 00:00:00 2001 From: markzegarelli Date: Fri, 13 Jan 2023 16:25:31 -0800 Subject: [PATCH 32/74] Update vale rules (#4055) --- .github/styles/segment/exclamation.yml | 14 ++++++++------ .github/styles/segment/links copy.yml | 6 ------ .github/styles/segment/links.yml | 2 +- .github/styles/segment/relative-url.yml | 4 ++-- 4 files changed, 11 insertions(+), 15 deletions(-) delete mode 100644 .github/styles/segment/links copy.yml diff --git a/.github/styles/segment/exclamation.yml b/.github/styles/segment/exclamation.yml index d2b6d6d9ff..7704a7aa9e 100644 --- a/.github/styles/segment/exclamation.yml +++ b/.github/styles/segment/exclamation.yml @@ -1,6 +1,8 @@ -extends: substitution -message: "Please use '%s' instead of '%s'." -link: https://docs.microsoft.com/en-us/style-guide/punctuation/exclamation-points -level: warning -swap: - '\w!\s': '\.' +extends: existence +message: "Don't use exclamation points in text." +nonword: true +level: error +action: + name: remove +tokens: + - '\w!(?:\s|$)' diff --git a/.github/styles/segment/links copy.yml b/.github/styles/segment/links copy.yml deleted file mode 100644 index 9847e1054e..0000000000 --- a/.github/styles/segment/links copy.yml +++ /dev/null @@ -1,6 +0,0 @@ -extends: existence -message: "Write meaningful link text." -level: warning -scope: link -raw: - - '\[?here\]\(' diff --git a/.github/styles/segment/links.yml b/.github/styles/segment/links.yml index 2e448c3c97..a6611a8120 100644 --- a/.github/styles/segment/links.yml +++ b/.github/styles/segment/links.yml @@ -3,4 +3,4 @@ message: "Write meaningful link text." link: https://docs.microsoft.com/en-us/style-guide/urls-web-addresses level: warning raw: - - '\[?here\]\(' + - '\[?here\]\(.*' diff --git a/.github/styles/segment/relative-url.yml b/.github/styles/segment/relative-url.yml index a09e27cd0e..66e2403138 100644 --- a/.github/styles/segment/relative-url.yml +++ b/.github/styles/segment/relative-url.yml @@ -1,6 +1,6 @@ extends: existence -message: 'Link to Segment docs "%s" must be relative.' +message: 'Link to Segment docs "%s" should be relative.' level: warning scope: raw raw: - - '\[.+\]\(https://(www.)?segment.com/docs.*\)' \ No newline at end of file + - '(segment.com\/docs.[^)]*)' \ No newline at end of file From fad87b453bdb6b7243ea5476eb4c2cbac714c0c4 Mon Sep 17 00:00:00 2001 From: Xavier Vello Date: Mon, 16 Jan 2023 11:11:23 +0100 Subject: [PATCH 33/74] posthog: update instructions for Cloud EU --- src/connections/destinations/catalog/posthog/index.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/connections/destinations/catalog/posthog/index.md b/src/connections/destinations/catalog/posthog/index.md index ec98b2c578..8f55a3c062 100644 --- a/src/connections/destinations/catalog/posthog/index.md +++ b/src/connections/destinations/catalog/posthog/index.md @@ -5,7 +5,7 @@ id: 5ece242d61055a0b1bb2e103 --- [PostHog](https://posthog.com/?utm_source=segmentio&utm_medium=docs&utm_campaign=partners) is self-hosted, open-source analytics product. Get the same powerful features as other product analytics software but keep full control over your data. -This destination is maintained by PostHog. For any issues with the destination, [contact the PostHog Support team](mailto:hey@posthog.com). +This destination is maintained by PostHog. For any issues with the destination, checkout the [PostHog users slack](https://posthog.com/slack) or [contact the PostHog Support team](mailto:hey@posthog.com). ## Getting Started @@ -16,9 +16,10 @@ This destination is maintained by PostHog. For any issues with the destination, 3. Choose which Source should send data to the PostHog destination. 4. Go to your [PostHog set up page](https://app.posthog.com/setup), and find and copy the **API key**. 5. Enter the PostHog API Key that you copied in the PostHog destination settings in Segment. - -> note "" -> **Note**: If you're hosting your own PostHog instance, add the URL of your instance without the trailing slash in the **PostHog instance** setting. For example, `https://posthog-example.herokuapp.com` +6. Enter your PostHog instance URL, as the address to your instance **without any trailing slash**, for example: + - `https://app.posthog.com` if you are using PostHog Cloud US + - `https://eu.posthog.com` if you are using PostHog Cloud EU + - `https://posthog-example.herokuapp.com` if you are self-hosting on Heruku ## Page From c59c1dc353a47966fc3ec77cff6423e3a1063557 Mon Sep 17 00:00:00 2001 From: pwseg Date: Mon, 16 Jan 2023 13:22:26 -0600 Subject: [PATCH 34/74] Add Audiences and Journeys Guide [netlify-build] --- src/_data/sidenav/main.yml | 2 + src/guides/audiences-and-journeys.md | 76 ++++++++++++++++++ .../images/digital_marketing_lifecyle.png | Bin 0 -> 241839 bytes 3 files changed, 78 insertions(+) create mode 100644 src/guides/audiences-and-journeys.md create mode 100644 src/guides/images/digital_marketing_lifecyle.png diff --git a/src/_data/sidenav/main.yml b/src/_data/sidenav/main.yml index 791d723765..4217f13768 100644 --- a/src/_data/sidenav/main.yml +++ b/src/_data/sidenav/main.yml @@ -43,6 +43,8 @@ sections: title: Replay - path: /guides/regional-segment title: Regional Segment + - path: /guides/audiences-and-journeys + title: Audiences and Journeys - section_title: How-to Guides slug: guides/how-to-guides diff --git a/src/guides/audiences-and-journeys.md b/src/guides/audiences-and-journeys.md new file mode 100644 index 0000000000..419cccaa2f --- /dev/null +++ b/src/guides/audiences-and-journeys.md @@ -0,0 +1,76 @@ +--- +title: Audiences and Journeys +plan: engage-foundations +--- + +Audiences and Journeys are fundamental to [Twilio Engage](/docs/engage/). Both let you segment your users, send them personalized content, and show them ads from platforms like Facebook or Google. + +In this guide, you’ll learn how to choose between an Audience and a Journey for a number of marketing use cases across the customer lifecycle. + +## Back to basics + +First, consider the following definitions for an Audience and a Journey. + +### Audience + +In Engage, an [Audience](/docs/engage/audiences/) is a group of users that share certain characteristics. When you create an Audience, you group users who meet certain conditions, like having performed an event or having a [Computed Trait](/docs/engage/audiences/computed-traits/). + +Once you’ve created an Audience, you can sync it to [marketing automation tools](/docs/connections/destinations/catalog/#marketing-automation), [ads platforms](/docs/connections/destinations/catalog/#advertising), [analytics tools](/docs/connections/destinations/catalog/#analytics), or [data warehouses](/docs/connections/storage/warehouses/). Depending on the Audience’s conditions and [connected Destination(s)](/docs/connections/destinations/), Segment syncs the Audience’s users in batches or in real time, as they meet the Audience’s conditions. + +### Journey + +A [Journey](/docs/engage/journeys/) is a logic-driven workflow that progresses users through steps based on conditions and time delays. You add users to a Journey with an entry condition, then users progress through [the Journey’s steps](/docs/engage/journeys/step-types/) based on conditions you define during Journey setup. + +As with Audiences, Segment can sync users to Destinations at designated points in the Journey. Unlike an Audience, a Journey can send users to Twilio Engage’s [native email and SMS channels](/docs/engage/campaigns/). + +## Engage and the customer lifecycle + +The customer lifecycle provides a helpful framework for thinking about Audiences and Journeys. + +![A flow chart of the digital marketing lifecyle](images/digital_marketing_lifecyle.png "Digital Marketing Lifecycle Funnel") + +Engage Audiences tend to be most effective at the top of the customer lifecycle funnel, where brand awareness and discovery occurs. + +A Journey becomes a better option as customers progress down the funnel, where a more complex strategy involving messaging, social ads, and newsletters helps move customers closer to conversion. + +## Choosing between an Audience and a Journey + +With the customer lifecycle in mind, use the following table as a starting point for selecting an Audience or Journey for common marketing use cases: + +| Use Case | Audience or Journey | +| ----------------------------------------------- | ------------------- | +| I want to send email and SMS campaigns. | Journey | +| I only have one intended touchpoint. | Audience | +| I need branching logic. | Journey | +| I want to run A/B tests. | Journey | +| I want to re-target customers with the same ad. | Audience | + +While these suggestions will work for most use cases, you may need to consider other factors before you implement your own campaign. Asking the following questions will help you identify the right approach. + +### Over the course of a campaign, how many touchpoints do I want to create? + +Audiences work best for single, one-off messages or touchpoints. If you need a campaign with time delays and branching logic, opt for a Journey. + +For example, an Audience works well if you want to show a single ad when a user abandons a cart. If, however, you want to show an ad, wait several days, then send the user an email if they’ve not completed their purchase, go with a Journey. + +### Do I want to use Engage Premier Channels like SMS and email? + +All messaging through [Engage Premier Channels](/docs/engage/#market-to-customers-with-engage-premier-and-channels) takes place in Journeys. If you’d like to send an SMS or email campaign to a customer, use a Journey. + +### Do I need branching logic? + +Create a Journey if you want to incorporate branching logic into your campaign. + +### Do I want to conduct an A/B test or create a holdout group? + +A number of Journeys step types, like [randomized splits](/docs/engage/journeys/step-types/#randomized-splits), let you run experiments and test your campaigns. If you want to experiment with different groups, use a Journey. + +### Do I want my customers to receive the same campaign more than once? + +With Journeys, you can allow customers to [re-enter a Journey they’ve exited](/docs/engage/journeys/build-journey/#exit-and-re-entry-times) or restrict them to a one-time Journey. + +Audiences, on the other hand, admit users whenever they meet the Audience’s criteria. For example, you may want to retarget a user with an ad whenever they view a page on your website. In this case, an Audience works well since the user can re-enter the Audience regardless of how many times they’ve already done so. + +## Putting it together + +With this guidance in mind, take your next steps with Engage by learning [how to build a Journey](/docs/engage/journeys/build-journey/) and [work with Engage Audiences](/docs/engage/audiences/). diff --git a/src/guides/images/digital_marketing_lifecyle.png b/src/guides/images/digital_marketing_lifecyle.png new file mode 100644 index 0000000000000000000000000000000000000000..4b99fcbaa764a3b6f70173703d73bc3c5847f229 GIT binary patch literal 241839 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�kY)4F_-PK+ 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⪙DBL0ZK4JR6|tmFovAbwZJYRR?<~r%?D0OI`n!`zmwilxv9h4 zXcCpNlOwdQ@JCE0XK4oye05!D+_hU-gbYtr+GBn3UA`dWiD3eF48t2@&tb^w7MxAJ z*l^L6&y%a}NI|K8l&m4*fWIZK1Lq1~yW*af-UFAPqcGYHR*9YWee7$Ebrw}B+DO!h z+i1=q-n)r*;(sAH6a5gq&#)?|VkkexiNgxu5COz>3O*U~d_thaw5G@*?Xw<=D_@_1 zkhY&(M{5#W^~*k9OWf^{SmV zHuai^TnyQz2~ zn=|=^w!Y_&qdB_8^+iM9XZ0(wz{zuh&!qZ*Xo*V0(C|37c4x?X#xbigM|v8%fiJ3btLVqZK8*V*LIxUMU#td6^GpeZQ|&@AI+je4wfLx(xy208&|#-lh)kG8ko_O)X7en`fR zGf&jfe=sYyC?B6-xlb?`F~)@J%4 z_*Y1g@<{`>Z@c}Mo<*kqss&H;RSSXl7gy$IxwXwy2YV)4>Fndx#xKN;yD(jkK^=jE z4XOR46+)7uDZbb%lt1D7iNEUGFWa_U2()@E`LyV=(xx?+h02z(v!)gewBnk*J(X5` zA1M~s-Kk19$=14^c=B~z*Ft^I1dyx1sxX2Ix_0!|>YNbs`jF&VU1t;4@%TF8SY$Y-*cJ=sh22ZT&dX2neCXj@7{)@_sZ@9|++h2|HWRv3-#? zE8HDT63)rYy?Q{NPuXqSzO_#ebr}D{|9d9iz6a?Wsz@h92A?*oCs>u7#ug%%;YNguqVG0!nWJgw!uuOy>$i+nQtq!R!o%6 zq{{aoL)mzwb9d%mzsnUDC_mF^ zHsgS&dlC!2e1BGFtLz1!2h{Z#=yUY`fuhTZxOmH2wJpOo0E>XAujijLYrBbfi2(FH z5Pq*LCTxoY&LHU}Lp<0RI^OGN*EKU>z~ybMyK$Srq$kJ6hy(zT|IM_4s!qr0NgnT; zvS#?>kZ0@f+M^El{7-(To)Aw*0RBbuewK?{u(Xf@RD%3n$hr+oci#m1B1#{=1OO%p zgZ_L#0LE|&vhBj3fZrx{YZ>CYFKeS~3GZ+!-8q^ri(?_DY{hh~8NVm|2_};tQlG-r zF3&EnN|nTxD;;Ga&x#bD-EvyiHgY9doxY(8$7Pv3%kt|^fY<1#%aA<+K6Dmi#ncHy zuBqZzTR{32peLb!y?iUj)2EQH{hiQ{_+m&w8p~5IS{<2=NCXx_fvcLVFii6YqgA}K zTrH)%k7xVs;{^}?aOp(YhF5>4?UX7u$OzfB7=Ua{SmH2{o1eZ82D)SiJy&XmGA2a^ z%xLsCLuM?pd-?cJyG`RW4!j;I$B8ybgq3%bQ18d5l-V37M$Lb#(wQ@#mOU?y>2(Y1 zk#(N~fcc0@lD^U*irh?}i!_`)7P*M&g-;Pi@87~VFMm~aMabJ+bq=qE|68I=O#2?Z zGlxS8oO?+6Cn^hiLfNuECzjZv*#@HU)I<5MCYdYE$Lv%#_~l0T)U8X&czUmky1CZ> zZ7i5xwYQv~2>Hpq?(@x7Q^U(^BLJ{JE}$Lkjxe5L`ymtq=e*og$s$C-h4c9BlqP<4 zz2LrmeeO8)gz~w3Cb+c6D-N`DzR^8a)p%{`5faZvK>kD-(>P=cbXkM^)m(`nIbU`^ ztWC$0G^{@nSLJ!*nKA@xs;fIy-T6X&M#vpbEAMl}@s;R(0{gT)gno8jY@RdB3uT+3 zgDI@Vqo+gc@{)NCr3rkuY!n z$U4AWIrv*krqSfC8GhY$dxH=bqiQv>EV{0G+I5fd-+89)=7KulT|`5b=#mAYW*@`; zaP$hT>xYj6_4y-jv!|nJDCdo-3;I<*K}5%1qgrtNBcQZ|>L#m9r|AP*#o5~cE>?PC zo-+dq*plORk3Vt8zV;3-W3y84lDQaO3olj|Q+Kw>9C<7@UX@D8v$muc4zc97u>Yv# zc|@z-eaggh--FPTAbxgiQRj+WWg*{y++>b#W?3Of!2Ec@;I)}hxz}JmHB4&@FKs81 zh9*p+7@$#y<0{2zGgL{7K8~ab-Rtuk5!)9eI9Iy1498@UZTA6w640 z-Biw!S^(0}J4P~j-3nOa%wJ-t|9W2BxE^6?c0YjSC1{GVlzZHK(lv21OXj0>re>Es zKF{{+P9s1T3M+@0#s#P*r7zHCt*6)?R)%>WlccV+bh&p|c^frVx$hEMt0T;mSuE)~ zBRhq^IIY<9{j8}=28!czjbxoPPo0bdaFi%$E**0eYS~5Y?-K+Uf!=l~3v z?Y;5|Db_VcJlcaiHIpCQW?>l+fKl8I6V1X64z|F+UHta580R#~UoB*?hpX%IvM}Hl z$e%KTa>$AtF%VgYJ#|v^14QFvb~Oq(%M`Kfcy3J_4P1~`Dnct)1 zSNwL171?J%6|bO%e^t0-ATphH;01E@-+_d3n1@K0`wn}$AM~wz`X6bk3r0!K%4E(O zzDOQ$*+$;Ra!SjtRn*`ZHJsfax7i0)(de&klHXnOYr6kiLsf$b>XAuC+41}@WJ##> zxVk0Fc*z?u6WR-^2t8?NwvNih-PvWMG9pidU$*JTee8xJ^e zRBHb&2r^%n2#CjK+0Srr#U;bFapbk8xhpnH7R=8x#+etd)d?E#cq07VHx`+SXG>gM z3<_%hY2Dz>XMyeaj~?;Z>6taG9xkWY;@C<{6i%j^NF`jO2x)++3;tzpf3QtmL$~Yr zht1evDeM=yQ;QZZ!we~mN#q$>IFmp0`5f!hucy_yfPjBlKb4Nh?)ypG*L4Zmc-+F( za0cL<;J?ndK7R#GMg0JiD0PMkTMjvf(+ap<+p3$vpnFdW9lF5B6GSb6=&j$tJ{xy= zT#0^c&-F&cW{(#A_8$R*N;-b6i`|SWna=ApgbqNNeC?cGH=5R2mrF|iSd0ppS#K~S z@2y*lwOD z(H-m9MDmnsuT7j>dO44WT)-)}!#h%751frE|b zEZWB%I&+<@EfqAueKG)3B?3%P&T<(vlcL&l-Bf*=`$RN{afZd3%tSR>`;U=YHBwQ>x(3^bae&R z3NOlfVDIAMs;| zt^&a=s2|4aM+ms>(3~NaV7J0oFCBp4XjSp6tZB#Ebn|_s*}Wg%O~Cx2Jfa#MU!-ea*Q7%t6H~kYP38$yxo`D z4U_sI)nrvds3!JxB~WT^)V>7g-W8PUK?|v_eeS7XZIexh==1A20%%8QpK0R_gneY~ z_hE&qz*7a;@b2)z-a6oSw@5;+fWK8N^dR!7Lg0b*fgjhK^l!t{48u>u+;0Ns0Y3Ra3G=X8bT>hsFX zkSSzug9CMh1ON?SjUweGpg2V6tI-~ruNo=zMjccN z)XK!eCyHX@H#}}ZC=sJ*(j}+5RTujCu5cY{f+>SymLZ=k$c6%t7sGu&90KPRGg4}#niY}CL&p`bnm~Ik@vSX}pKF=+z8p@| zT>x&fDnl}PX}A_isi3njs7CB%9EK+d2dH~M>FNLq@B)?}3D!+oI={r*p96k+-5GQh zbU0G$BFDTZwJFEEZIm}MP(CfVCktTCGLLttEZoCwBIV9g`D*|)z{&LpW&`E`h|!!H zKPmT>p??`pjeRLXHw7afhRQzgpY=1w-2?uAF95*a7#6PhU7f1QhQERlTB;#Fyx9$3 zf^1(ZN-KyxYfUqPKTHigSHh&W`MGG67#c!gZl-mb(u-pSplDhRft^_5l3H2t04#GPuE~}UVLzF>F z)o^GH(P(<;FY_C_e~N_&N|5oMw~PxT7RZE{!|iCp|7zi!sKfhHHOLJ=SanJ~e-9&q zB@j{q8w$o!&AL>?ksHJu;1}(pK#-#-%ge5R`Qfi9EjqFu5Hj5bEi%m#6Hc4X#vF@D zO@PC4ts*LC42xQUU6T)XYk6(m8xN%prgMlGJUjJZ zC=Gp$p8v&(E20@zg&a#dqGe4!3PWZ&ZI?CwQ_uT)F3r<2(U!@RBGL5JKrnXG`SK7(KLVeXi8vwN>XSU=-mcTF;5fvely}ys@EZx(A zG(2f$05WR>orS^zV_4yykP&*IlBh}OV17H~q3{R3^5gublNJaharM{4ZW7esQo_-D zIZJLW|MTr3w2%@^7*6g1$ri-9fYkp{IqHFNZHIK%?a_l(O1}gHBr#kJV-)~vzSU6@7U<*B>2tfK zeJ^*{1Wbg(XbiK;TMNrlQtYjXj^teKRPtm@+3VNpK}WZ+IuT8F0^Rr1es0Uq97ui? zLdFq^wcerz!Xr;_mt`F5@1oPr14PU4P=$ny*<`zj5!!pX%5d}`@g!mY`Awf@B0Hr= z)^e{3povjFR}y1`u(Od{;Yx-DU`vtE@lb~LT=NkcAjgr04#lyuH_`7utSsUCDIyhd zS_?)9qdUvegaP7}=|P{B!P3CrO>7FH!wUiGyR7i`x3)#ydYzGDf4UeuM$x^DL$zsW*o(1bR zZ(e-5{?D9y(EH=3?4SLf1}JcbSZBR%5=JiT-TSF>KbKmDZqL_AsQ-6Teel5>ZDIcZ zV_V?o)jF{J`#4LhbZ)hLVUb)3diY6HV8=AD3Ku8AV4i>F(O3dmMxW4cJl019XW5|e zR9~Nl0mZ}u`xdG%77V$05sF<&J~T8GS&;Kkb_^kyH?WMr&Fv@j7s}kSiWy*q7&rOt zF+uKlL~?XhiRp^rbvsGSuuv?b49q@t5d`*mA>7`G(p|<3 z894}26xf4TMEWPy4(SDs8a7Svc*h`TUMCN9j0Ply^cqQa9=XI3%5Pr?Ra_%hm59^+ z&^-sDGn|MO=C`DDbZ!@BoK*po4mQ?r10AGDWj6#N1_Y(rUy4reT|G%C?!`NW%m;bu z2>!^fNE%?~fVikfPoBb{PM0Puyc(zC<0<#zIfia5|F&+yd|HY}pfB32SiG31)F>yn zWeRik4tAs>XYMUWt?NCzuwO+ipLJwyp3H;Vx+3n6d zYElT|O7%fcdHl05L>iwBd9l^7PKvB_t`?cU>S&#c!;-S%enLA59a51s8qLiO3Br;= zq{SKN9Wp^n=lAB{Bg_$6%_2Dx(>8hjdZ=J<{( zpp1W-5Y2lfyM!DTas10wrcvYaT`y&t$~cvl3gbMp8W181JC|~Qcqf$vie+o}IVlb@b#$5T`_NDZJIF=;E8) zVfvi?Kz7d>QSPqIg!^|(ZR}q9*k9gJ#+$enfmwA%VcS4|c{5Zv7i1#R2u0+jQJy|+ zTz@hJJ!t9G9#~vX-xJlh_HOs?id>pkC}Lu(XDFP6#^pvCTiq&3Vm>9bzTg~@wB2S{ zqwDx(iA=BT)6@Hw5f<*WXdpcrk|J8cq6krB7Z7ViCl8N1{pY_tATW3v-!gKrlGdF1OGt?+%#>PAvFNz+;RJt$8Di%cjAQ8iQ zk1<%SFQ{0Q6=K#HYE9N9bFDmL<}oD#)h~;#ZiYlhdPmH4;@%X$Xb(h``v}6~HV(*B z6_)*{`=SulW$O*PRJOV0>N8W=sh+{qBuq>ro|4d)R_kBp%fzN z*qBjZHYG&)!@f|SFkG64w?M<)8jGfi+Io^gd}pD0(y1>AiZq%u(n}F9&u0t}(~RzZ z{iEXYAeAihLlxF5!!2D0mi{DpU*Wde{Ws~TYM*uq=Ef~BfY+F7m~Eq}RTXOqj8p9n z>}o+c;I`r^U^upTIG+8zJ~skoUzEmz$cfz!cd6)scxS_(Yjtd<5E-Ngs7QQK%iEPy zghxa%oFR#PJ6*s5 z8!Ur)jACdLF+l`E%ycQ^#!x7SVqQJ)?L4v`3H%Gqid2g{&kHt#nunEdBsQh=Y$^9q zAnQD%mndQ$trb%bz7Od&Tr|VCTInoG7))F^I#4Fd69HsPyDN~#?I{DhE_`8Fg$i@% zQJNMkMX^p3)PAZG5&+Y|0Hf?ZSIP-zv~vW~?RZ*Fhree)A@f&c&*h9pUMEp>2xVx^ zT{=}^uEwLgNq4RX=*>hl31(1tb(IGLt-X}PmcXz7c&!#1{`?vtL6wo4V{ViNV#EHU zt3{wHuPn0$dtE~92n&#KQ}&CwV}VbJWt)5wv<~^KKOFr^Xe)ZqwHA$P62uk&La^GA zR8kaF9qv1f9-*~Rs<3fi7aVmU{1}#jD$#$?WT<#ONOO(fla*iSvLqe$sJi(LWAsxi z7ffT{Gx(Q~enn?>SMDep)EfSO2a`t5-+<#mIu$zw^MB`54#f6P;6{3o6n{Oako881 zERk?ONI{o9@hDMI?yDt<=f9zT@W`8unqT^VmZsL>!vjHob5L&qvPx2zP%TIPX(L3( zGYj8ZARJzXOcG3k3z@2Gg1ganXo zfK;FC>nz;|pVQBcDgbdTcF>b~I$*n$PW%msg^+Is-}I2xp5+D9Qa?wj;Mm50i>paB zhyvrV3ocdLz&2D7!XYH%l)h-H@)U&h{hER9Sv-QIV}ftL8A9!=uxX}e_4(+9wDt~I ziDD@%qShrJ+AwEU#AsH@XQaxbX3(LK{}j&AcRTH3KByf~gR`m%1iF;Lr3X`$+Z@EX zQLvQP?IX@Yz(7w#1n`UQyv5xNO2}Y-pQTc{uCkXwq2z zj0zANd-d1BoPMGxPH{URJ)k|5y|!%2p8NX~svpSq2f(E90o5V|<%C9O(&+B7@>P!- z2m@)_U2LhSh90aV7)efIJ<flFlk^by09qGOC6n7&-es<^(p3jPAfe&Em_ zYDnnm+54S41y+SB3oaD`UW*%q`aKc_fzNujDT`6Bl~~n3Illn(7RlG4?#Z!)d2oLC zLc^!2)h8}Jw7|L~gSs&T$g52sK@5M2-EcyVrZU}4szv79%3A_4qDC1lkLs0er}+xy zma<4@)mTMz?*lp8`)buy|364cr5Fq>BNX2Y^t`?isoRbZYs>9sje>YPVbVbRWYyAn zFj2`uTK;VqrEQbQ15GznNzABXpNdwT^)gj*d)2`|R(!eudrbS1EPnIWl<_?c-Bkgx zcb*-8YR1I9A*ng&Hpc`CJdrtUrhEpZ;*mRqj9|B*)b9{oKr?X`vg~yMioWl1TXSwBSR2|;O*2JcR z)K1KQYUswX^m1nU?nSQI_h&udRCGRW>U@4FRf+-6%Klv?5HMa$$*T20$YM5;1Pqxx zHTFf4?rKb3hpHcwN1eur{f7{+md0JKcrDi3VjjC2|3-BiBK(pV6MKV zH4+b(WemyWvi6e<(cS!?4Fc>wQ$I=FstHmrz?O#0c`wt)g6qzV38*hV$hL+e_WGC2 zlQnlDRj5A8j(j#jjkp$64I_=M9!e?buSOO~a$~B_2V7$&-Or!uD27+r_(Ly$I^nh# z$g&btxcgV&_97!m^h$!yu_idGxu8u_)?ci&PHSmT^DDnUTj>wy1}>8nkg)>GlG7_D zW^bP{tonqMWMI37M44GDX6Rr9Yhn0e-hXz*K%=?*Z=f zIF7ZJ$?Kx83TzufSnDv2z1Z;VlFN-Tg!o>?T9q-?k5Sn8d9b{h15$KNemamY=kCl3 z8V#O}NRaq<+x-t#Y%88mw&-^j!`!QbX>G!U6Av9GD_#`5w@oG(I$_FQyZ?QU(I zx~Ig-X%a&eO{nhb-%Dx|3*uXFG^_JNtSLWCQw^5|BmJbvLAu@S0=w@?zDd*RjM3FU z)-nj;YXKtNXtanF;6_;uyp}YlkWI`2qDLpNB*J81N=!#5K8k4lo?v?UF$acxOl;2* zid9k|$@h9pkD!~DNXVpwi>c5|rbphwyo3Tn;U#?*FB~|X!;-t@6RtRCv&%1|=DYzb zglU2Ne=`4~?@uZ#nR0FPz3C+HzdfQE5YcR}e6&KN5ghW&o`$DbxOr!$32~G zTi>vtVW@C_c*#H@ki0HY)JX!jXL0=te4ViA)Hq0GEAE^9ho%gmPdCUIca8J1-{&ND z=@oTHdW@JTbw2Yr4@SqSY%W3XQSQl^;7jA$6#$?P|H=j(bWN}acI|^w8Soo+D`8N9 z^l_Ijp8Qr{isQS#o`0M=JgcnJyfErYkRX?EIG=?9hSQ8bG*MFT?@)`8-?g(GrAq#YMQCz`$ zb}e^}R)6XZJ{&ihJ)Y4_;0YTxY|1205ug3Us&9^|-*9<*04P6$>`F`*His|H{IY4q zR(QL1`oCaL{hvN84*h?E@(>`3VWUii6_u8JST7Ixmi47t@jH_xm z{}*q3ef*A+j1fH(wte zqw$)jUo`ig{!r@GlcOCI(YvI0s4FEy<%^sAk*xtQwE2S#D@3<>H$*C{EFZ4#Xpn5e z#}z=pFJ9%quX3V_ia}^MpYDzO$H?=7NVC>Q88|xb?No!&c?APgDXu~4KAZo<`rp57 zP|r9i&UJ13|Lm^(mq{swa_=#JOrUY%tC&Q{WkOs~2>1k`T3;~RQ(6Zth@FWA#O{hv z62}DPE^DA6+@4s-wI?j~mrT9|Y-5s~hP~w@T?;ot;U!92?2C5o=ezIQ*ANprK1c@0 zQvge2OkUZ1|3ob*Rh8X>Vh*p?SW8o2YiGwna1C`s#H#1w`I_%eQ>>nMKb~+p`0915 zSDXzo@nu2zn&1!0Jb*0s>eOopc#%b8C`%y=-^V@w`J+TJ3XzizaNB{9a;oZ6K>(AM zlywy2rwwBXoZqzPjpghW#770xfkuFL1W&druZIi#*zHunu$%eCPXCK zFz#eG7mD6+Swpv&GUSHVB}PCJv5P)AiTju!lUcs~uNUqE4Fl!^>=p&tDg5tqcjQpz zZG1a3v(1He)n8>WH7p)ZUe%`lyxRU9WuK(vhlz_q-B_;r?p#23RNwn+|3Q=hL;(qq z@Yh(;pXL$_4MZUitxqp}QVZ*vOx!|=!xITBMhd3ok5m1UPllv$S#zcAnH5j= z&(c4p*^45>CO%|ioFyaIXAJWGs1{L@7CiKpQ^#$%7gowtl`KvayPpNXEtuX{k}95| zl`opFzxDU6W57LnnppX}@p$B?SsKG{=-ud9>_~|)@0pjjyJEn+DCLd=JR-yQ+b9ZG zSQQ+|j&GcdEbguR=IxYE9h9%Yo$S6jyM2yOxKB6#tx>@Bb8($Sw8N(#Toqf(h%8t- zgb+vsw6EvhkyqV5+v6rRctL(n7`Al9P#LM$y$%t>pa63hH9(W135`A4Ck%h$^7@}c z0cPp<-SxiP2%8H>#VAFYw;@rRE^?PbSI&+nRuD{E>-~nXoU;LHvsBc@z-A&pie%BK z6g8OW_GP|Tvd=RdVP801+I|No{nFG8(MJNr?IIn!SND!a*`S1Ch;Gou^%Mu_pka|I zY!y19amNqAl5(=JuFx2>i@RUY2jZC-ELFcH<5BEsEtXrRk+XsGQqO>GM>2p6D~T82 zuhLElquhSi-&wVi_V-^I4#HeJEOppUmbk-sRJOZ)qtkbn9m3Pnf5Db#SNDRUkkPkA zZZUO#+WNcX(uZ2sD+BlwQx+Vk7$tcwZ^P+jgEr&Tb$qo>evieMq*{AsYr!MG*v#_Y z=s)(D{4_9+cPFtYB99ZYT|}$oq~)1h%+(Un3v9i|Vdz#e6A9|Ps+ZZ6-0#Dhl*fb& z3b*=UVPqwwltG&W?Sa!3@}G0f>wnR8zEtyBRMKBc7{rzs*b}UqUfMHTE!>=9!>z@D z+3k=YP3ybj(FQ>m!vG%azhJ2u@A~q)midt5c61xuf}|o~YJE~_U^3zuQNnN)Hgz9I z7%D>8zRjQ%wck$zyf?v?V6>*`I<9tk%+BAls0+*UCw-9* zxXjxKGAMt>>T>ay2*rZ^*ap>ddb0bccRsK54m6}KS(8<_HnV8dWTHP^Z3ovlzp+J1 z+1q@p{|v#Ria8MjXwxY&1e8CvIry&koUwy*cQp6>=*fg7B*IKsjeiDaxBRYNSg?#n zHlT^-b%;>D*89NFfz=oJpZUB!?|;ipZW%%9|J_VK3VWEep;MKAqhLUZ$xLNhyZ6cS ze=cL-oDK^StP&gu!%crtH4}5UE@H&NOOyH9OSArb#(ENJqh$_6>LInowEZgz%>_!A zdi~TV0G$TZ{~b~GPO%Dp_G!&6^{nIh4|=FUE*V>G zvD~MoOWj}UW?BPZ5IMLkwHrE}02)pxbHZq^5ou_GY6!nTj~d@M`|2-5Fq;}ipE9k5 zb}PXC^g-H9c}}O;rmrVfjPmAeXS}G$FVJI{<>p4+hv~GV?EtKsuXLRmyYMk-=Y#q$ z7EwfgV?UaY?pYfvS2|Ov&i)yhu`G30S;JJ?f)&T6V=Ei#pd_S}e|{~0ETlfU-lHBv zBh(x6|N0{$-^=vwrpG^0P?WcwCH%viHMwtK-Vb&!2&meVrdQ_3=Bp~K|LJ*sngT+x zX(aPz%URu%^$QYmS;u)Hsmu!;v)$UC<=maBcf}u$HZA{be&rss-m7m_Brdr1&Hmvn`c&gN&gRxM-02(@1UU+& zR=_>6?;#)7!<@!#Em4?q*jt%8@(FI4DS^Jc)AF9^O*u^^d3d4FO}V0)CXoa9VOE1X zDKJt!B`IZi&}zSWnN7&Bam&_VfRooFz@G+uJzMfDs4n>Mviky72|OPLwiMcN%}-HN zIX+{?=$W_;Z@zLnIS>=h+NO9F4#y|7>d;(J2E@Nhwr{BK z7>d+r0_A85S-Wk^MO;f=X?tP2@n>dOdfG9x#DwU?1i?HL(=6&N9-nb6n-#;wjbwe( z>MB`8g=E04JP=F}iAQnSdUVXzXRG!9BkV1s>ROt1(Lf-<-JOLy!QI{6-Q6X)yE_DT zf;++8Ex1E)*Wh{=$=>gK$9K+m@2@!)%-LQ2lyr4#gUs z``&jQ4TR7~K_?B%y zxU{z4Kign)_Fe&8K&s;#vuJB#bGluU+DQC!N|YEfCK+n@-zZ2~XHXfwQ*0FgQ1=1$ zXy3wqGm+XOLHubP0r=PlbTJXi*LIgCflQ>V1_w%xq{G6MK$G4_@X> zi-b9)tRi&*CIS*-2Q4)CA(vv~|B01?_Wbbr)#1e9cN%@Z&6=;hEOS53%@Pdb|8Z`3gk&J;y!~ zWy_K;r@-%WMVa8YQBONC&|pp*zShcCCKJ#GrUZl4`FtG)Gm6m-Ux@3FvOCroQh%o}%4YN=3K)5tAu0q&jX$lqw| zx0wC`7(wk9#1K;>fzFP!O=S+I1VI0JMfW83^PX@rt$J(!voQ>V@hXe8hQ8}s`;yI) zdnPPjA5)RWyz1x=QAE$>0_oQk^04FjF^Y1nJpt}5AkXw{!2G|aVW)(`0%>;T%OTK7 zEH4y?86<5Dp=a%`Eu8GDI9Xver+Yqu^*C*4vN~}ZrCcI7k65J1C1%tfH@PBk5QeUf zz?8Xbw>eG)c;S5f7hXQSZb0loVx?Oj6VhR{?%q5thimb@6-3Ty4waSchDB7(Y>9E% z0(H%bZR_ya561x5&8*7sbPicvwha>=z=qSZie|&`nefIi8*3=~XR3~OYIPvME*a=j z0rEhR8w=uNeaI6JeHu5GQ7~Dn_v|oBW?ySeJ)ZwK`iAba|J4P&!05ib|ks@OW z%1eujjaRumZ5+_(_WRRw^Qzux$~Jils?c(*oMW0$G1GI2b;Bjt_RWxJWTb8{>&vY%hH^~{=IKZ1tixew;acc?7xt^> z&ykU#=xZuN$Kr_WmkLJG6kD8sU|~mu*fN$;126!8(ZYd@3rtfB)1`lFBR#7BYDrw| zGN!+LLHVt6=;&@}YR2WN@6hOh!TM4mmR6#$VdXou4B(Ff{AsESRJ6+!oW~Rap+(|% zU_FXhAGD4dz3k!fd7W@Xur=^Xo5!W45@Cj=DT@^|^{QXHL1jI)DrYXc?ZTC=!foVc z=1lzk#i7pvi2Q9I1m4vtl?;@>Z49|Y3=PP--~4>Yh+Y$Lxbp-Lw4iCvt$aUz1*!&0 zk;=BZ*;)SD$~$Ir4bz7FuLePYq2Yn9%|!X53~vjSn(1shLZ2CLsr0qn@@8`lfo5JxR) z&7EG7-6^gB=o$t*hn{{i9BuiB!=(`aSoxEs-$`{d{mOrMZc3CB6oL3o0>Te+u<-8@ zqzuE5+8tLdsW1d&EJ9Goy0C6uOVla>DXw!@YhXvB%3*sr(GMA@Y%{RQZf z!g-E16T}g9f6V68 z>^oVT0nYywTCE5Odf8M%i`Yn&X)VE~h^0WXIcY>jPoyt1$|Cxw3W?t%h4;+wo$6X= z9g(DpBNP7gXF`O~H)xiI4Csa`;??$E2{a?uMEwP{-NEXl@<*CKQRCO1dR<^Ms%f7qr)48%u^*f);*KY#kaVbeGI5F2*?zY|Q01ROUl zQr8gPe|!HQopwDS0O9i-67u}t2}TbCjvF0P&*=Yy3!d-r@fZPk{&#{S-X#c_BUk(X z!37>5eC+rPi1`0I!NLHb&Hg$Gjk;Fg2u{H_?FKx6yMWnD$uzm=?~MRk=f zT6wDn;mc?0f6W^=VNNIOtYnIa+Ag!IREKF{9}g!A9Um1j`Nv}vA7K-I zeqNOkyn0KWYr5d>%^d$KVTLhXIJ z@fetYzHUQ@2<#vA@N<|&d?&Gs8f-$g7DY0_#5i>HUr_@Zh(rPvp@L8)3Z4YUM!Y>-bV)0MX_Jm48U5QSW z`6CqTVn-#c@!TC=)O%Bul>c!A2*94tC0SAW@gtr^?AcA1&phq_8MEDY1c-Q@8I=#AW&AJ zXmKX@bniW8No9PD!aI5EV5oO*l?Ex4=&a!==YcDd@!1~VXaUoy+YCpc9J&-XqY6> zg~$A31?=qKWvPD?z4=`V_z8bvUjm{F&1o!IzLu#~eu3P3A#z+DNL;!Xnvu!mF_c#C zx$jG%s7+unl?9f1v*q+jbIWD2r~BKQUM|5;2LoXrNr~4x;u&3BvRkTHn%Q_XP*OOdRF=P(j*>cCuY4C5l%4`^bK5N!W>z zL!)S09*q{_@>(~-$M5cUF z8+PY;Ximz_`*K2e#-S9)c%d$r^;BCOdMfhyG|m{0XtC=-;;wA0Hae)M(_qq~Cl@ zacsDBOf1UPvC@85x$hXT*5({hp~G#}@U7gi{<`5S!_5;>dYUKGBfsuLL%`h2nA70X z`29a-WC-XZkOcp=4ZnqQU=OG3DNsrnu#q=o5&A_&gMZ1YF$fVd8yZ-;ARQ;8>ns{i z+O8OLx<)Tg^$v>j_FX&2I^SR-|JsprEz*!y**zzrFk-wsx7(9=;4BDRQT+fKWH4Qr zcwk*GkW2m)G#%DemNUOIA`^0{vPaZYH8=QD8xs!)CNwxY^kZtRYx_xXOyOiP@_3vIeGd@Qh>8y z2kP^q3Y(uPcHyGrMPp(8QB$Cx#I`qEMu{;yq+Sw9@T0&>X}$zPXjZT!xNZI!#aGxv zrj9C1FeZ&~x$A0g6DJ+5vqWBvhK1ffx|HoG%EUBT2D;!d!yU+!ho zUA$nO;`e#VVmS@eH@NPkGFLI=rm7EM1z|r2O>eB5MB*(ek7St&R4S!Xge=5BLSXE+ z!27jxx3L-r@C?C?xg5E_Szd7x<_qbx9ZB7=wdY!BecCGcshE-?DW26mW8sPR@cXNF z@#3>uf$B>b!sU~Ui`-0oUX|1ACb&zXGXwwW7JF=MuoSp{e1Y_?hYd4-;XQ2O5deit zH^nT2@&{6?VHyOvF)fe8CdrYt-o_M_8Jw4ZtNziT$Xx2AQY@5NO<_B(*#QG7`z$Tz;zSDE!=!34MI+ki z5&X?%0lu|0W*LcQBKjyCSa#f2!8Ch~()tQ{{K4fy>xPlQ_P6!)Q^wA#oC@v9x+oG8 z_U$HtR{YDJE_`dBy=GDU?_5zopPR2Y3M#QoR2QA8Wd8VKYG%MWzxJm)*D^McW#>>J zh;%$5vp6$J9PLTT8d?ou!_m07)=LSpZ8jUP^Z6|7OM};rq1K#kl4@AL$VnLEXssU{ z;*5Q%OoF!Z;|p0XMLaa(GGkJMBv`WR{{1nOzs4 ziA_VavG?l6QXOZ=6@Qy~RFq7Y6Ph8&ixC?>I8Gq*@kskCs~x`}M! zG=(ky^?kIVQuwO5dW>B>xa4{kARV7Xi#A76M!1LT3zoLqm6PzwOUDRNd%5A)WJS64 zSu|CFOfK;q6-vcBLKWZ@o#F+UwTe3GU%xT~+V9C>A4UN>4sKf00-q?QIz>Q5{Vo+} ziyLK{2-l=HC<$%zx8s`1IK?w`ks(kfUHMx|kQl(5# ztW?{ly)f*Iw&SarE4v7l3XG&`dc@Duv?FaE#blvtJk!kFk4k21tIM1J4M(ZvymO0> zljj>QkXye+bE;j$p$bfrV5xNobR@jy`<8d-Qe>A|J1;Q?+heKk?ablSl3~>Zqn*9f z5yMdqN2P2e$t^Uh6HSW8q+U+h6O_f!+v~Dw&{8`+`zwV*W5gX0>W2JGGTJ20G5J#%jZK0 zV^Z+aA$c6=OmP(c$6{)%r$=(9>@sP9EJiQZhP&X|Jl5>turP9o5HKoQ+CD4pP;e}8 zD^RrhILgc!Cq6XZ8BaY{bgYs#q#OH1Ca?rY`P)%icYds@+jSbQ6K_fl0c~4$s4J+2 ziZ`N$%uxvs_>rSSSbhS51-6Pc9&to6*LU3dKJkT)3?o!$5g(44vgD~cfKKQrmyBBk zk3#lwmjBju+!!S*S+Nk_!=eHgh1Us%?nu%!`Ys~wCYE5A=ps9j)?}_oAkSK^;`|i> zcV?t=TpJb{O(B-yUH;hF(=XDe!b#7rWE#~1k7J&Ubr`|pCqA>KtR2gU=?4c0R9qr+p(}~JZEIM1R@(%U!Gxr=i?U206&46Z?+ge{ zq1;LF_Qcs8)bmoFEPW(<0O9_ z({gdmcr7ef4l?rGV5OF=)QLndQt3QyEuMk0WM~q}IH5K(n9;#V3B{;ztEzaL(fVPSfJs>-NB7E-sD~I}*Es>tD5nA2{}I zpcv`WIf^TUXGdl=kX(X55!a{$`~>54%#|8<LvTh|IZgeC(rW zFL%5@7)sJ?yPzpcskIw@V%g+vt<0kdOT2Q2o)#8w=QXc;6G4#I+Y5|8gRX*DzyhsH zDMQO&5G{ZE^z+6oe}S3u z3?;AFCkeY%XvOPw=KE4i_nIFSZ2{Vns7@+D?Xu#~p&9(ud^k8gHd!Diwz0l1^l3T; zg^AI5*IvYLy54!co2>P>3@&691XwtaEL7OKY;h8UIOM$As6%Z|fHA5Gd%tV{H2aMMoQ>5efHY!kcys$r2i8C|E8UJx{3Z#;%P|h;}}30xOMJ+97R7 z5a-5N=?L5lDsUyBFRVgJP5+LOX^^wg??pvfnV!W+^?2h(?M@WBJTxu>BFOUq6sTfZ z-1z0adM)U0#f-!CFJPI1U@>O$N^oRX= z$;IxH`g_=^+G->;cG`%M8wp~3L$G7^7g}*VB*gFe%V8MLXoq(qwGqBo9vqo?AqC1E z5DOTfSjX;qp5$z945lBMRZzufEoNnzRWP_(lV71J$e@)EAkuCT&Cq-ipBiL>H^qYaT89V9YsYnRR0%k z?R(CuyPmMEvq@Z#$CW1?|D6||RLUPFQW?mQI0e5{FB0-dtN{iXj>e%$Q1gKCWQZuj zWKbA8?u^7E772D@Un-8oG!*;Fq2oa4Uh&dq2tjo{b4>rxM1L5BvMH0uMywMJ0jyJp zyi@(r+IsUSP4NAn3S(HyD)W#aa1%+fpACouBFP&FAmy&hGBn-%xweUow{PYlv6Sj8 zz%_zRjV$tPyj~=~Mnu~ZU#X~7<`lv~q7ES-BFKO>e618pJzxylmd#L3(7iIPuSrP2 zwwVICB#;tr3B8EbEtDDy$P$jDli@Q@gAb&z{z-$#V@>lUFEfJdLQ)=SWSk&|ye99a z_n9Rt_-fT3mXU~ins}G=3XpKdnG#N3OZ%O`a-|0Cu%}qSqey1y<*v$jbp80WP}Oyj zicXJIRM$c)R8LVI5IAc~<83H3n^^WT^b?dS+3$W@I11Ojch^dz4j6r4J}NQ)jcFSc z+RB2#!7s%=3?-_Z_*KWHa?8B5)|E1DbV zk=nMjSj_S|%2})4BYBG2Xz9InltcVdCVPoJfHCtszsnk4`0&B_uZXDqe0R2TT6wYF zBb)nzIRx4Ts}u(uhBVIy!_i*5qcO`7bRE(=BTW2r*v2Tds-da&Ep)K#>(nOM!X)ld z2MGpA!-EZc6w!U7lYxl<)!1;Db32%b6xR<263yB$U^lkjS;%lLGKNw45i5;JR2H{W zx}J-l9vhs*z+i|6W-;cKO;0Xz85n3*a?cYo0O|0U(Jr4(X!M9WK>}L>wHNQc5jaGMUY| zOx0F`TZY8VS)cucs$P&w&k@#LN2(zhUP9LmXy?_hpVC&6YdlM*)+S)2q?Z81BIOFH zE3hHT7>QvTm_m&b&E-Dsek9t?hCm||)WT}WGl!ifK?7H6R{E&Y;Pvt)cNUp0|x>bfAqDFZFnW{x%`T? zs=%}%FbGX(-L>m2Gc28_ODQKm= zZ*H>~5ov7JtA@>ZD4dPO?ob8?Gd&~-4dR!P(-fra_H6|FbO<3(Mr@h}dVh#1MLcQ? z`qcz0(EWbdr5d^Y6{50Ki~1v17~jI+CXa+yV~a(dZYh05b|t0b`A7T19{I=}!x?`8 zAB)*Tv{*#Be_r1`Dg}|x7hIGl2MYERYiWIPKZQz~$kB(z#B&T9>9}y>&p!nNP@fiw z;8z}Rh;9SU--dMD^;4>DJs7-D|V?Ki;d|jkdkTVW@4e?mwMrC8vF=H3^1b} zCZrHRPGMd4X2)-X-i}3YuNnnoSpk8+xGtE|%LU1!aTV*KQ$wRA%!gnZC1j%c%7-+9 zD2>Ob{d2@dEkgb5O5|gcZy|ZGK$GjimyBK%oh9R*aX?4JX}MXbwd3t$G(q|Mn6RCP zNW-_;(I5df0z2W@AP5UR3&ODH`R)Z?kO&f`DDqQusMHSi<=@8~%Sn`RhZtxir@fG# znv)H}@Z-5Z;2_o7OLr<$z9jOik8>Of{iq)LuKXFBqU|SJtwY{=auZb(eZ0TaTch4w zU?+7HRu5RSu^%z@v+LksI@YN3mroSD;C{*(`||cbSP(8cSshsm?;(%Q+{};%TE!-> zBhA|m#=_LSdB4SGqIxSDMY-%r^>^G+3fXzT@Xq1NLF8R69uyZ9E&C-3jG<| zIN=;SPHEl;+I0p=NQX|eZ?TkbV^iYjyXme&zb81oh@YDYa`=CJ@2_o9UVsf;=)?!X%;;R{Lk3XGGzFuRyTrEPn;OVo=K^kv( zc}uA^tK1cm)fSK^4F_hUGx5%$%t)$3{RcM4E3E-+if;$x@0vdKJkO!u<5)HTOo)rvX5U% zOEBhivsnFv2EU1%XXUIDeZJ-xUR28kZ*5)5ErAJZgtQ6j_TgcwXqCZs5xdR0>IR-8 zSA9TMI-Lam>WP8fEwYd^mPW1jKpnZ6u#LgR@bD*TL)pBlqd+11#F_M|zER=^CcV&Y zB+CNXj9WvgyILiYO9XB~EoJJ(r%qVBT^IqO-KThBF<9>X*<4X=!jB8Dejw>M*?cb4 z-e!(TEr?n&=5DH;u`k-WCuvLK zXFU1RCW33yk#c2=Gs^tVqkfI z;3Lmq2@M|u=qzfS~mhy_T8*tb|v^)(e;)`N$~O*fA5@Ry64%-hU! zE*ml#>0gnKKPz>c!azS;U$YrO$oQ6dNozKF|2#8T8Xrj-wfZ_`AQstj>V6#C{=8*l zG>}_eKEz>oTO-S5MU@O&qh;Q7%6*@_X1|ZyXfkJk(wB_OcZpiFv)p#7^%+`dnz&FJ=Powc|1r&yUSJ(@2Kr0Rnmh@gr)n!gbY*hzf0G znk2&Nq|biE^;cl~a~-ZrAcK)#U2vur-u*=;%Lo4JXGh+kK&)zDZqkfOxrXT^cilLi z&$Ic8)Apy<-Q?EOmIg_ysSDpIJWiRFV>L3|riF_`K)mVbJS+mhu<>Rdmb%#;nM=;( zFrhF)(q$hP+v<0+hxKKiF$>QZ{e02rmFP^{Ixb6IA$e4WG6QTDni|e^*Uy2HgF0Tj z#+n|zpER5I+%-A4%#ck7P9D%Vs!yxV>+F+doEN>dB)jXo#-u}@o5Z$hUI6w22UtE7 zfDZHzpld_~uzt_y$*-h{*QOMPZNQViJQzt|vg$MB;IenK&3|UOU!e|cNk^_AvKhSC zIH!CYKxJ=Uz&-CDRQS`2qpf{6M1lMd8J;p0bM4p-7*W@~xB3u<3gupdgZ?NJu#P}TPW8;|& zL-MGg(d@1JCEWUbwyB%+Jp1U<3#;HnAgJZY;PK{FcG8f-x~Tgy$9g(6Jkw}C!|;Uu zc9jtOaZ!&z=E^aTa$)IA8~gO_sVw+qVxRZp6hU*5+6HLtKx;e$~^( zDaCuI?xs*`Cxkf~nh|F*!DQ;B_>G#6A*3hR8?R}&Y$dz&qbJwjc{uI9q5yWuaeOff zAzN>f8*Vv$o_#AcgHWLW5TBkOG}@zuha|C2>0Tc>UGC}UX{_+QCb@}Ro@gYkJ*^s| zSZ$i-$_U~Jk{_QW5Jbz!k2#)tvV8(>GiqWycRp!G#*JQXo7kRt_BVDi`)&Uc4~<_r zt;=39Na}#vIW+g!BFlG~k&ZZBcsm@l8m;KPuiCWcX{AllBq~bMo?LC2dOf4!1tyT; zRFe=zbiJ5hcjX<-YnCD4#xhBU$ni09I+U1e0&BGG`=k`AAET*vCR0siQ7prD8m%-k zTRq*T5PmDYjgwskuQ33o&`|)djr87AVFv2T()sIz=;`_$4MWS!#{pV3%<=}qkn`#) z;Fal5>#o^1+8He6_>GfZw`lZ-i=TOaOskZu6ZR67ZIS($#;ox;(=yS0dXzm4;dPIy zVHCjsQh9%h#IE&>RY*m#h-He1h6IY00uxjx5T4GBwnMEs?6BO2?;M4By50X<1Fr^o zQ1opN}N^Ps|X)Z>h7jy zgTtCkwrco^y}KmwLOD`)VjCnatkKAKC@cOtnKlFO}%gcwQa7p z$8C1)$Ik;|EJMywO*_lM>MIq$Z8D76N9`nkvm3S8Fu$6TU!4jTpC&EHF2)(&hv(kk zt+g)l+{xl?Jz)11!=f<;cnxXQ5{gBTjD}bbRwCcduPd_1i(=p{rQ71xR=8RPuCiOv zwR?_jqt!_ry~3|YmM0)E(X`&JSEyyiWDiB5`XDs9mu^$NONDNjq+p_+ytGLaE9Pp`D9yTg9>(LCK;r*rJ~>OS=m+n|{f>^$p#o-s4QuNcr(*=X+86 z)qwa)!+GFboFgQg*H+x!%D^}kp~9j>1uW*LG{x`ERu5OZ*Q%q9og98hktuL?Y8@6o z&)VO3sB~=n?c8=Ai#?zrcrgy&j zJ)s^Nro|83^(_XTuGWmYoO*sU2K&h3QnVq1Q?Z==y7Xr5bFb??ji=#E)PcR+p>+^8 z2){^df>CN{)K5Q;4$!IJW#&Bk^`u|Q{)s}~J_0okzPP&5oYjbzG@Ie&BwVRjX7w4k zfmzgDDVm!Er3Sz zxts>`sHtJ0>Uvl6Y29XVGRq_Ly6hyHbAFR*pc-r0(&ufr>esU?&X!SrMcZo~3q6Bb z(`v_5$I}hjYO^&al3=t8wxY4eS@Xx+_H%AS@;EU*UJq>szx3-q6G8&ErnN~}Wj!t;bUN(C`3}}wi_nWUl*ZJDZV$+5lK|7EDfyrI| z=(TNk`y*X-GmiXEkkt|kI2BhOo<$A3rJkVp=iI<(Ao8%URnW1JeLhfr>YenNepFQ|$A=DcUa+oZ z)pc8m_BwEB#XC-ISg5Gpcs08;UuQc8 zlrAwkU#aQ8t(9j!nw2Sf@{4E8_1p2uF1k)_LO#ikrD35#qxlP|hsPb}NZ>VJ3bOIV z2_xs}0iu=MBHq&l`5k!xD&Lup|2Loqd$*WNRD8OJow6J*9AUL~6LImG5%kjED)fH7 za@*ygi=y>%;F!R_zQTDenGU<&=T$VjSn8R@m|2qxRK39E=Mao(9_EA@W7C=Wre3S{ z#{H2YH9m(RcJR{p_YId$ub$`_Odf+SvvG^3ij5ao&&iB>eD23@+&2e2o@2kbQeTsv zEgUsv62sKTmh+q!=t%PdkTh#gt8fDfG>r4}$u3SEy4IVViQE`nQd;1yK0^~x=hO>H zMTfwj;MS??vhl40ODLZyeu^=9nbie1J+&e}P?lb5QrPNgGF52*Kp{>H(dmka#sP|j z92w;Hi3XDTlFLlq(6Myjs`vDEShodx%em+S_n|vCV!q|41UBgR93DR4Y8YwzPk4m| zRO2YOxmby2eaqFzA}4zQZj|7w7oG6kI4gYqNwt5_PVrKj0(Q3D0pP1KlV~!r+bCw1Ng6(!%Y&S4QeX5DsyA|@<2Tv}^F0yOy zm|g|s*6ksvU`;Kw4%HoczMj#W*-u4)LxDlHg(Tj9Gzz*O?xvii>uOtNKi@xD-N3%6 zc}OiqrGK_xrYx>|xgGD^7(gOH@ub#cXcU`|H6g-Bd9`m$9hfFbuT3f&Q_&A1q)LDj zlEmK$cEROfaRb!_)+&^u}G=R=;5%Vpd zvX3Oooq!1Cn`kwx-&&)y+Bp9iEiBf z(j(})yjKy66ib5denCo24!0apWi9%BngI~UJLH?4udrV*cnEt3y#lwns`@IpD1bGZ z8pep4jAlIK+J}We@lDBUsbzv?J7wnvcv4MQQ(aQNmpJe#b09VZsli=<1Jq7oa|Xw!)l6Qu%Aj2R0|>Y%`@ zm8+J>@LTXL@#{1jD^1-{H1!k*ydla{be4mZA)?(WifIHsk7fyezFm2HN{QKu2%@P| z|1|68WZpGo7Bo8x28(i`gqnUqAAMzdLVm|@VjlzYZT4n ztv%hor*$B?R_bIXyJaQI?a2HHkniuL@F>~ZS6{uH#`TeLUbl`U)+|5})CP*YfU(4{wGZ}R(lnxLCz__aUPuH><&)zSUj`5LY^*? z;YXXWhd5%Lj_f{F0(x06PmJI64AgFoOWaZWW^|+Atb%Q!zfcX24B@T{3peQF=>Oe5 z!U1`K{ByVq=*H7W`Jx#4qY%TLh>XHfkeid6kt7Z|`diAoWjWf22mz0l?YAkb+0_I+@`IYJmfvNw`!#psD)H2cb#$;Oxe|Z&-N^ux)XAK+b4AGjU8L4?Irqe9l=3H5YHpHh_@{@xz;< zHHY5}jomsCh2x|568fCGh?GH8Z1#CZPwPAo5+v9;gzVOvfx_ZK(M03|pH@p0IqA*W zWj{uwczM{%wf?R~#wk;Yyg!o#DCv4Q<6k9Ofl_76n37s~;-JRuDoLi$gm_}rqU(6~ z>v-KTdpsTvX0~DBVC1&JX*wEG_F9R@bUw+L8)_>C{pu<;wK$4gbALixsn)M(zuCjS zz3F^vdExbW72X--0PiuJnvTA?u{2leg=DB0Qs7m!gK)&b=YG1wbv(}HcVTir$ZsNA zIgXW;emoQju;lNisbOX=;*K>j&cNt}o)UeLdF5OAb#6Qo>VnS(JC@F2%s$&s$krDz zWb_$sv>3_TL<-F#5N;{5rA#edgS!*5k2`W@*upKM*@=1ECAxijdsMZWvO@+9B;mh+iQ&2B5T(|? zw$Pp=H3s@ic?ag{M(p<|)l70hqXH(i6VYN79Nwxxdr@q>;@voU)?$j9%>Pnv9F7~H zW^%nn8;VXIxs0HxgWrhH`&jDrIMcL@z@y`Rr~6#WebpSZym+5lq0u?}xP*CNsp~zl zP9SUj6}&^}m}N;{JU;Y`l+3pw<=j`lSBi4YyR5Y0JT=cBd3 z%ptMn;SmWZ-jde1yA;2v}IM@1epgxp^m z7;jf+svLB-1vwuQEv%`vHGawaY3Osw$zt~i>li(oO01~(#Zfy+V5vsuChF-^h|m<* zcyb}35+WR+f(*!oY=n~rxS#v=F0V$KYQldXNC8HQwf-EF1uphdMfe!o|K=u9%Ymih zv>@d{=6S<83)O6|;GQznWEfa?vypx@PIhIgVF-2o>WiGq3Z~UPw>UkdaDg}=4`r8& zFpA~Jq#@EfjdnOi_6x-Rf@M%9KHcr&h97H zfNa!qA_Dr`GustIUeB4VubyORNM5nXS04--Pg?Y`)jiNn6Qg>Z5VA>m?_VX=dNR5+ z4AQtC`ruX^ew#GP=mdUTE>%`%Ff4yGm%vzeu*A$tzAzba%HLCP2RY;@v~JgIuUeL}#JRt-+RscD_#Q%!&@PSy>hA@%g^k~O0_eErV(8O%OY-n z_&)KKiyQ!ZXw~c%+cR9ck)YnLD_!4Uc5mFL!T$ykSR$NOyVHG$rF`wktB6GJX^G?r zTh|9JqB)#Kvvwqf7IteuI4fZn!>v@kZZLZ}x+=@t z&;w?95@|Jxzi&Q#rkXxj;sy6d?fubAp;>0q89K#~*K&*~KjLXG8goins-B}4F0exd z0>nW)1hdNkd3_v53j%{`NZPx{tlROg>8(0nIZgGNJv~7Iu$RPvA^(5;F zP2I<5R3@_r5JK~vM$^hGR&n>1zjd=^w^9E^L$m3@y*v@FPV30`)cd9+lS8{k#I)hKR0NtMaMs z)PyVqX!C3#LcdP^n*Q2L-HWO}fEgKKt9fP{jv0WH4q&RarzR(WApki_I58vI!1Ky` zuBPAYkf1Q!lpkoe!352t0Dy%MFjH_!KYP+E6)%F48>Wdj_tg@NnAcJl741@ZVx^iU zUV;rkw20ZcOGF3CK(;gw$oDgg!m>?~u(FhEnp zqxO?SgYoxV>&!9-NNCdKibG=Ok>+v)ts^c98ZeRP^n=_&8+ke0asi>bd+di>;`m7Q zhJYCzG0LBp#$wx6pOuX1j#Ui(aq~+Ns1)#OEDj^%>iSg{)tWbngrm)CaJLkbC-2AyftdC7%J@rR=eLKGCoFlf=2T|(0<_NU`A3j~SG@Z=P6QoCR$D#BypXwR}CWm;(0ql8_=Rz6Ix72--L zx5Ol}1I|$P$Daf#%Ovx(@-m;1G2}-qLOM9vB$KbG@o|>X*#5*I4_O5Ti|8uuT^tCQ=5Ry zB_v4UCqK-++d7eD5Z#5ll-JXw0MG@o1^WB@xXBgdL*|8a%vUQ2h+YU#2w1of=u4D4 z?3+Y86kti;j7hDZvl4|6E-H-a=^1P`eo4d((4^H%##8^0K!DOfo3XEz?}N|B4bCrj z(h%{btt4@F*n-aH=dihgQL+;#gP5zjpkWc>YEe>p7yJdvWDL&LjpKjE4LD{%*u zZX{S*e8EbV7+n62s-w_(CG)~X2nKN@HU_K73HgkTrU(RfE)6y=23fx-K9)CNRvz~e zcPLgdFcxP+A{dUr4ae=9r^G6qy%lE-NnW_a#b|zb9M#`Adw`a$@Nc7%dkmJ%re@igU- zyEM^$;3CQvtUf)J7YQIhD6uxhOh;7T-ki)nQcDS|6Mu+#RV;G(?-J@78uRZ4G^K6FcT8?~MlX@6N;R*y%^&4L5tt_(@!BrUbQJo_nZ%-< zJ{)Rs6HDph*&rFcBs&qEmXu1j>AGXB-9;erZ+Q zMgL3|Na_%ia|!i{5v*LY6heqymdn7zPskyywq?i9h#Vp3uwEjUSCzKM6LfeWs(Xsd zPAKF1_=t#E;>82uR!g_4#Q{*TfJ8kN+dFh~9>rSB-uc%9AH!n$ z$5q|BNk0D*zxeB>|KqLy@55vAui9E5jGy*@#oGV*RY>4pj7mXKe%Al~{oiX|WdBWq z_D1$p|3}*Me^>v%TOvUH-E);ylH&U}f8c-XvLPV#kzR05a8=CwA8Y-MdNJ4RNu_kjvb z5BEQ}chP}3xn%Yvjx4au2d212h~MM5{;BlnU*UNQ$_h2lN3oIx5;)|VI{(yn$FFN} z4{VYaGQyRm^~s*KsLI~iM>Flz$mUuN0eP&+3#?u2O>m(goetcIE;Dj*;*@$#Sa~zO>K+yMUqcn+g7vF#7?KDL@9ACDpY+Ro=tIg-1JF%7Q`R%esv}69|9LiA z41Xn*4F))Yf5|>cp~6kP+C!XL`c4jKrVgcF&UYsbj(KW2BAgWO9>w)Jm#;^d+#j#4 zO{=WQ>zZL{l(lS|+w<-E->*?cdl-wRElI*H7xgv)h$v-i0GL;EDZ8fvZk``%3w{V}Zped_9W(nxRd4@yAKW z$gJFQ2!G1)QrSuTwrt$H^XQu1%%Y%<2*I!2j+Y06UIgFM|9&7Jyt16B~?$(xj4Y{=1mG`sh ztcqX)CdOCkl;Z?ZA09sx+EhFHOa{{b)V;gsuh=bQ<$n6=qeFTqp&m$N{h+y~I7AmA zQZ_`VUe6F^>}OL=R3W$~okIA5WxCgKcviey3z+)x%@%HEl+0NNDyHY3k@KN-{TQ%# z=NndrA5Zm#A1xN0J0Dgy9{rv10lRay4657QZat|m+|c^!2HbbZkEp&i8!gKamFjD& z1TNg<3}f5KL1oB++hZV?OaOQ7km=F5spIS;rA;6Cz z?}!C(j0P3yFv@Q0@BAn_HOKZ*+)Tm#Rp+uIr&r%)ek$>jZGKpAA@`?ox!=J0l~=FR zz4Z*ryF=g{=_w8Dyp4zS=hbB21O*|@)xK3_+iZ((*-?wy7}hboZ7kC_5ez3S1aN^^ zMlHDn(SOCgl~^A`v_;d474%oJu0;T@O0usms)Dya2lpZJqutkua@oBuPnC1utkrMb znxHAcS^jwc9f|(Yjw#%ET8~vC%MAi|nWmixN9Yl}w)`zpErhA1~gyE ztX?u5bnz&|^Dg7NGq|Vy$b|4bEic}p{i()7%Ee&bY$$6S$CA$6Fw`%~0!#jPv8mY} z0Zh%oH*k)D3gHb|$&DAK9#3F2m_Ly*;4kF^JU}kyuPq>&rDpkC1d|f}2IARo7>nNb zdS)t6d*8#~qjcEIcqW`7eR!ep46EI1K5f2H7RmiJsWO+Tl7f}NW7{rl7NdO^ISysh z`Jk5~?dFgGOyjsiC>1l42N)z?fkWiu!d@R3C#jNa&0oXKI!{nJ0pTDkg&`R)ZZgU2 zf8Kv1i3tD4QZnNIwF#V7`5Pa_<^@#h&4)%3$#}0N0sAC)9epY+R)y~c%IHQmU@-gr z&XGJ8gA`nFsX{iLKBjNO-m)dNMHy`CnX~rj$UZyNsoYW$k+p&|!H+;7)R#lUnS$$f z&zZwgHR$#NiyeD!mF(=Q_>r4;PXLop$<^#*g1Da!X3`mjj(Qows5|d{I6vdLSXcg9 zqT`(R!o<3H`pT9e-)&y-tjBa*upsZ;y=RT(=!VRTVw1xzpyDeqlQzLiW=>_-#+@<2 zvJ=rLUTwbq+bmjEnF671Ng>uYuSS-x#?9BrQS&*5EMB+Fr+C0^T(wFax0*i9cH{`J zwO1a!VbuO^U~Hre8ZsD5y!3X@?q&3*;Co#Q?u7an&w9z9Z+T3pai9!27fs%z0wb^x zcJv=LmAFU*4Rx>~28t}dBApYUV#zkPhbZV-oicq_U5_(o0Tub46RX!L8bhi(Iav+O zLVSJ;4Jg=K?Y*s|UPUjf%{=IJ@C)WDlI|_5!>#8y){)!=$I)s&P6ml8KmjA4xU*HF z^_D+rn0RC%{H03nGp?8TaoLjq&RzQK;ls?s-m4qx&1??y3_5JdD;BLtVmn0n-|Jat{zHYjnpj4u$4Pog3bh` zHe!a~PGKY&34 zr|kk25>y3n4{B@k9T3a~03;FtXkSW7^_RoQe|+?K%TeZESfswjWETIo@GEnz|W+Oq&Bb>_31@9i$QFQsF#H+fb@7Z7+qAU4MBQJ#Qn%ue(F4Kslv*R81R3Nv00yR$87jajuWHVlh> z7{(%`<@**iQd3;jhMZTl?OOSZUpD_2zXb7Ns};m#(>oDOwEFBRpu$nIQ{6$OC>nm% z)ji4JMUf|a)mpVO%^}GsQKGzvY3DSVhe^-+HpkLHh=cchDL84=SMcn1<(Cr$qFe#| zV3{p|@&#mE9KT4i5TVzgIq$F>6}i%Js&Otf~{>ZGSdQYXdR^W4^Bo=X+`EtbxU zduwch-hXHNUZN!9tllH&5&!r*?PgdYoW0hw!Dym$QX({sRuU_Gt#s-gY1@5(g4;X9 z{colZ!A(~OI)BU1PgNfW zS6&b9Q^GHXK(capOe%_~S|Cx05Uu?I_LiRh0htKv7A8Dkw2r3j$~o5zzVg{oh1yqD zqn%U&+l|rG{{`MqfwD2lNylkVIwxf-orxZBR)v)ob{&8@(ok9U zApj*+*G)8Xprp85%1!ot_l{W^WjgsTuOnItA8A6XC!Va=8!l`W!Jq#%*d|4BWrPFb zZaSu8z0uCV{6{kKK}5Y^C!y|lbuCyW!s|Wh;2$V9>F}CUJj4!_16_<*Qn3UP(rS|c z|Lj4BeQSP`%YNTY-`^XLiQx$^u`}OPzWe_Mzsi{1J@86q4nd4$oVd2b;g?StmT~Xo zUmFxBsuDyKd~xxbYH(CjrMRT`IsDW00&LEe7DY>`+8@&Ska$7_KLkbF=G zQH&*K{weN>3OlUJ0rY^3qv-{!wZkPUZTp>3cYHbT#TEKdA)gZTI||ELa_4t+tY7Up zpz>6`Y6vY_C}#Ada_J>fr`+feL4&T)qa8aJ_{1MOe7UAdf{%ESdp09g+7NVIClCvWIoIiavV8PXLFTnJ9a5Fj9^3N%h^h|$dj6J_$6 zP*0w?9Xq%;Da=7YfFfoVgjRJMT+rc9Dxe{Je8p>dNuwVbD&yXdt-f-8$(|{rhs%V? zdTLN?-Fj^VRq-ZCqVeV;HWgEYH2RLUA&F)5fj5fb&J@Qa!#R!+VXUTsmq|}@ORNjk zcqdk$Xl=9CnC8}&ou6Z*850nS@2=A71Z=)@1)4_dIUTx|2sXI7`3>v$?LK(&cPlxn?i#aX^w!@*VuY+`v=09!S)w_!+tsl4H7?}8#%+z`V}jLg zdjR;0qoQYr^zjTkMt=nI!wAD0;6qhs)*uG%A{EFlFmB&PNDxe#wfJkG$!>uO+OgLhF!)WQV$>pVz+paNhq*0JCRV^I*eCA^ zG4wO90JAe#BE+ClI4Gex0yVZP<%_4Jq!B_4t0rB9urkvDX>QlI_Em=;IKu7dLDpd3 z9W8fJ2_y!OwiS+pf;XqDLBh*|uX4mS9o~wd+#sadd1csV8qdIT>KIymGs?awmh3bC z4bHaf$^tyvQXYY@8z~8kPeedR*;C-rOTt!LSBn~V-C?xgddpMKiY(!pkv{rj?z*oG z4AO_bmUy<)r=a*VPvU?M7p!bo3VBAf)+9VG`GPYmhRC67_{;1Jkbmd4M1w*=4B(r| zZr1Jrg9`IGHF>fa*%y=iN8pY|STRGSn&4F?R-|_;6aSC)Qo6}w*Mesne|G^-{PwRq zlf=S4Z6xm!fFZM8@*38s8rUlc@-J|Qk?Q>ePuMWbv47HzmYa= zB^Adi?7~}~(M6aBG)PVR_5Yw(f$`duFx<@jCI6CL;vh};g)=10$4 z@MvK8`?h-tpa4Ft@VDV}AumA?w})7svV=4G)yeuM%G#J6uREA>xSQJ7AS(|tK&QjrXT@QCpu z3|cE8x-;&-(b|ENykl^RXMqgxu{R{EL8Bxn4lb8zYOF_MD$ZDts=zEdYim-~N@46> zApAF|p&z?+P)S)|$*K|=L}|2o)O2h@%yx-J!V?g$$nt&Wmiu}nzbcEv4wp*!(iN0N z)QUN%VLRvHrd;%r>;(`2lT_tfsP|QWC|6k#`{9wK=x%J>sulueYD>PR(3|wpG{kbQ z&S%LHb&D3r(Io#-oioc1nCg?W(BYI}0orfO15wO6w!rteX^Zx?KC70Kj;Qj5!-%4( zR?uW44$*#wEb-y^wi~`o=>EqV=3daYpfge-*U<*mOrs&a9T6H$xWQ)jtks*W57Dvl zGGICwSn@@OlVY7EK8+RX3sL=xJYeE8d-opU97$A7yQ2`ZBmw@;_R?Nj3RAipT+5Eb zz$|tWUoop4fLc-8RyTPhQ6p#&jtxi7`I|#4LnP)M07+)le=-l_y_jI+`X=(YvPn82 zWq4J+Drf8oJ=9vBinXY@qE(}aRv9m;#MW=0e4m}us}Nqlttcy=%Ga!8+U!-nU`q)> zC?&QLt5{Fqm^COb4{?SOVho*FfQg2@9G-P>(7;bB>@zF*aPrp@1~25#z43DL>+Hqx zOoIa`5&|PSiMcltu`hsxZed6*_l=g!-*TQwl40zWYBT(ud^uFK)grQHnVFg0T1zAg z`WP>tOSFZ8I&_$JWYQk%L{Y5+9cY--OKnfgQTx0Yg9*D@vO|~#EYvy^!c)_6>wfpk zoxa_#f28a`6N`Z^R>VP&5M5FLL)f$9{$Zi6pH=@(D*}knQg5~L2LHtI|I?kJqJ&v4 z%>k3*nz=u(w5y1lG8|TqMhqbmV6{4d($^L>BjU}oiQtpK%$xpt!xoNrsas7JEdxi5 z;1*teSXnfN*^I?>Sg1Gvyy}uQ(|rNI%(_f$!Se(mDh+tq=?=nS1ifQL!sIRAZa!+q z?{SjW`aZexs3R+k6ilVHi&Dq6pK`}oXvt$28sStfyHSt_*3NTuxvEBP^@#LTtbaV2 zB7J4B8Rl}`Cy0QMN2fLGWu#>ANUUX!G@RwsvWhM;`_|0~E%f2}*+o8w_ob4+2!h)g z+R9rCypmXfq=6=>;&J2OMZuYZqL9$rGOBG@NQ0wjK(bO~lzJ6sYnizF+HI0AEX#j} zjz*s~wo|>~OJe3*UqmlkV_yi$UQwlD8Fh$p>XjUhcKMqSr&*VBmh<|xTdEP|IW$?5 zPv9U1;`xmCmSsm<5%=eV62mZdI2S&}Iw2)ay!rX`Dko$3mHP8qiR(vhAc4UPU%?2Q zmrhD_DhE9!>~}=TYi5zB4U`VRR|Ndjkh4NgwWdFWp8T`pBw z@;0s>oe}B6uk|B=xc@x9qhXz`yK6P)G${2NlpQFZh}e2;EK+C# zv{}TzwYJH3R*gZR_~eoYM#iJgB(hsNIS+>kGLQo#$Hq4vr%jOP;>9x6;?i$EOYQ4? zu9cOLc6(A~ZAcmzB-up}_JjC?*PA0^Mp=sME~++DjAowA_KIE98SnGdq5MVhmO|UJ z^JQjN$?_=UhL4>6#wo6PXxae+hy0uREJN4%FDD@mZC7CO^Q6X8+=$$8LXPXuY@q4N z=y$_2Vn^7?pB$${o4t+K<4qo#R`{-58ETyD{8qMAMyAIL_M$ztQBi1IkYEu`>Ikgr z+G(3Y`0%PF)F_t1BPK}>o0HyEK!G$O zm#-n^AR#fv0j48z^ z(2+dHrsQ$Yy&^pBIU>~A09KnJhTA5jP}O4KfvmZUZw$M_R@oOXMm1D3I8Fvv6%-NV zsUn%r5)r23K|7}SOy!u$a|hpah!dxop+2Y~DbOtt42QQW)GLX+N|W%um3K0m3s(in z?3Q1XWD^p3HsMVXC{~7XXhM!~SVz#Dj5PQRtigy!_^Mtd9Bm@>5PpzvdVN*?y+3eA zME2SGhgMt@-12O4JJwU^WnVSzHGg6my-`pw&9LP#=6!h8P+Lw7C#Y z4Ox2FO`q8pBN4-30;4|(SU9KV!`U7Xom2m10no3BH@o1{=@1LXTnx^BDPwwMp00E> zKT4VWkZ7fXiDp^KjM~&^N}4Z1iz_O`DVozrZhz8|IPz#!4^=(xWIkudZL8NH&kBb= zUCOUlctp%weFB@l8ZyI*&(>&A^K4Fo&81^bF9r&bZ zzPm0mx<$?U#M4HKbfxsHx=dxe_5(X@(bSBh!*G zXPVbPxA!a;?OGw_JDRb^Hn~@=7ex3*|5>Yc{3Yf)ct{-m<6}~TSWV}F>LcGNr_z63 zIVGj4LFGhxX9CzG=FPN3B5Cgn6qKVWPtzA$uSNv+-nGoTe-U1hn@S-j?$~v&d9_Vu z`|Z`AE;&s}#zcdVxPDgKnI(0+LwpEMiv#i^05IfP&?~I@Q!NSV7e`ht$(Y{N#WhIx zOAo<>e9N`|_g((;YkxWi{Z6&~`#Ik(rR+R#3xdu0G{WI%+&+(3gt^iIpz42?Pe)nU8d@6slWn>iYV=*Y(`)dxj%q zra{lEm~XEo@t|B)PC(mth}4PknHu={O<@F8ks_6{Ba3mG)6PCNB`)M@ex2%zwEet? zOim#zthHAP4Xl`?4PG6=YP<-1#t?@i6VeS|DBfy=-;=7oMi%5oQPVAe5UO5}x|li2 z9fjBCWtOrl8A`g8P5`Auas5jgL)q?Nf71+Q16|9Gx>pqDZl0dn*T)Cc2rA2A)nbHE z*j2xxU;E3W-bKOvzB7Cxz>x$3Mg&ONAPV)#*BjTBhU@S4t(UxQ`)?YkD4?xYi%Efo zmBy}`(S-^ldt$E#PJ9C031^f#;`a@WmAd`!73mpHtqCP(I`W5W)gb!^@Koi)6xw>Gh3*3gDJ! zgo;nw@aS*FH5cAPBsW_TI5DqoqFE~1+gR>)Kaya)LdCn~*d$w?~LP`J>YH^_$ z1sxA3qc^q@$CO@%y?P73S? zf7@`$*$>$5y87O2hMnEIJv?Z6(konf#fr#%oPG?;qSj=ke{diCWG=uJjU`Q-tnL*} zA=e=5UrdtyfpP)_Wqm@^+3vcmhdIg6rjCzb<=fC`!rKx5@j%O6U=eAy<%yax@Gb6k z)&iIP)UK$w{O2;G2uf)@Wy(gg38T`}NnoA9t$`0;QBI8Exy;9I_L$6)vDQo2$eQR@yI^aa+{V=!&m}BL2AG7~c$Y@thTCW%G|+Z4*SX-K zgb6cKNUeQjBOlZTK}OM~u80O+f`}&iIg-I(+eyacWWtkX^4n(uOosq2(N~Gv4|Olv zN7^M(LlpiZXuRnqw4$?e9l2e=eEl&jXDOQNURbiSzU{mN3y^Epyj*?myU()H<(oai&9%9hz0=5b^FHZewEsTiNO!(BslNp487 z98VpgOB08AkM#!*9TE=pxsyG4&8W-V@4fZhO^CfU;464kMHjeX*Ynv&lHrkm*;&Q8 z(Rxn}Ua$vysVcXG228R=1T2Ptp)}S_$FE%xDn&8Iz1kam!70qx554+`*p2!o4iKe| zyIzUJ4XBZqJanVPFpiXmq}n6!u)G;6gCH3kdlnEr9ynG)!bry+T`VjwfW)KfIw~e7X6uo9C>4Ns z<@QR>LWZLw2v-{*Bmbh>sYI>NYN?;OkK-=onoI8_3>b0_0>IDTvncNLb$%T{2ohV= zYH7vc#eS>-W0Mx0*V!!<2;heYnf5T5FbUpA67u?VcfRa0X}cozMLAM<-w*roIbM7p z@98TBuazovWnh@SfKszv^`$Crjr991F0oH zv$t_+P^nCPE^#&im{d2KBcvcSxYvxgGvq%-ToiDTKytD`WAS<`JwQYs8s(Jl90?46k=AAGci4IR;9*ttvm-KH~%93(k_T)D4e;&s<4T@UT z)LJ*;T6IZi7%Q&nfN}c}8h6+;@wW|nJ?GR2J>@^sFQ%Adway^B6V*g}&Z2!%YxGyr zLM71CJ3>|0iIZ}~ON5rww9t*>RJN-|aU*lNIct!YNMdlXtqBD|f-5v>+(vx|7?^c3 z0y^t3u-aqSm9wSrd_L)`d2Mu@cCjtBk8QWoyCe7kS$cGs+%Ip2I#{yogJq@^#}D^B zw|`#O;Ql9^goiCv=Trz}xpLV*^(UA)IM&t0vdJ||8dPu1vA}|l^&SZ}1TCX}3%ma6 z2CPHaq_K4!pFObgEDKgnWy@bE{BE8{{Jk(4m&P%lel#t!>^$~R~# zVrs~DxvpsCZT$Uh{lv@xA<)?rHloANzT&k^=z+d3!KP0rjuL%H%m|vBVnE(nT)EMz zRyr{XZn&8PPgJGZu0dgcAP}%}bAW!s4L_P$kZ>tIPjQJqnGfSIo{Y3Aol@nooS*3N|M83dHnBRoL za_R1A`0HM%EyVR0oG*nS>hDCegAX2>WyIJS&^nW&M&H1e5% zMaNS6aE*Iw)UV=t5cCjy_$Wd7)>{w^%Y13m z-hd?J8zZ0R6(pX8>jGyT4eZMx?{%ZYInjV6+rFmhTkk8T6DeMpw|6Xy&&$%VFfY4; zZT&(T;3p6)Pp?WaKnEd7Mg*M#AvWxhS2{y?;EiRa!B^;+isIn3MV^?s zlp%*%p6NU~i99xtpB+cT7FgWV4ZKfVznf#~rceAbGU2`~V&}X; z#=}#z3tPn#1YY=U=?P84%rGiNe<$03bCLe zzgXiWxT7_f8mM&fk*~FeO;6y66};qRGSI7gmd6^^#ys+8wg^%to8)Dj=iYZy?v|4o zR_wkP?5w|1-7i}Ta`c9S+l6^)f&5?Vf(4$25E^fL$?kr`7`R=2rfEu z*7YswbwH?j)?&;?qMKvo;>hs%uF2nI<=(Y6Qs0EbD)NutGsm5+kwFZ^g6uKxvZeoX zlTc$3k^ih>X5#gF@cg1CXxvt7$>CM$UU$4cv7^Q{?6t`M-ab=tf|UHBY6+$DS^;vs zVZjl@R+~bq4#tEvs6LqA(*OEG$?iIgublQB z?)l1cVP*Z}%)RHQnDy@Z9g*YHlbQ+#hk82)3UC8Z=bawcioyMOL8guORYZsJm8lQs z4NOHcVl)%Oy8UB-*qwLRE5-9zQPuB5dJyk#pd8(ubs3^r&OzjL&W;cF3}+mMNwDwj zalBF*c#o};J6axmK-QaH6t)thnhde1o zRb2Q=uUFoTiUW2Id_~#r1yiJ7&|1lA4C*SV+y^F?=mQkk6i=zH?w;j;hZ)C?u1FQz zhfE_=7xqR|@yS~8*Nq7qb5==Zc>@Mj^vYTmj6K|&|J_+=HD81hF>`$WPRB|Nh=HqV z+8sp?dmO`BfnKkZz?s1me_hZ@$Y+OTV6<`rqOhc3;cMqEZQH(D87T_MF*6PPn+h(B zjfn|~Da_q1D2B*$M(|Jj;FEH`jK+=~Z{MKMOGTCNMZ>c9`DqQY&$|q4bbhScd~)Sk z1K(Ne#98OQm#uve$*SwjU+yq|#&j+2mQ`BE-4oqH47NOyg?~=_zTlI=?`*Fpr~3ZZ z-_0v$E&H0!hM-~bBq##G zk>2Dm4`uq~3iAZ5fWFP183w+q^z~wq^_yfvC5qA!)(=B*(GlRJrr1!2b1Mmr=f2N_ zcQZd6gsIU$gn72RBNA@sMIU?_TfNinwRVB4q5Qii+jFS#TukKJ;XP(Ou92)jb4wc0?k+nytf<17kag^=~VP`3JnoXz8O6L=Y`i? zOy3@&x}K8l*V=JvW#4=p_kK#MDiGp1Xz)M#-8Zqo$eM;e0Cf4P#HyzzTP#0LbZb%) za!D9JvkHjqw`TOjd@ipl!dJj0O8fw=SQY!+-r{_SEV!1^=gZ0qtkaS1s7{h_);2#s zx4$kpypkRBJLAsm|8jM61J(3)+@<(GrnrrBEG|(IHP3%N%pyKp`3@P+AnoWvunWtxOm6*?1(#!@XNj1oY})Dl_`?c)7$asP$d^MNqD20f zf9w(oDg&y`R%se-&(0KkvM+k;m&Au*m*g1AW>EA&B6)&=fJ+!r`zB;D4wihUqj=yR z#oILKZYaiwe8}~8HiQ9ex~{F zWt2>U>PovHfvZ<4J1##aUW-Yw!pcT>E>}>_%kVnh0{ic-2+c4U9gg+O>F06)v81=L zAh=SHz+K+RqNJ_;fhW4;`p=x&zEurAF1;waWA6b!Ce z1FO;T@XN7w_1M$2y64wkPVbiz6fyCoPP+=ipv2`4D%qd>i z-F8lApr%Md1AC+Hd4wW6Y$Msm0yNt%^34=8_$k+!lbxEOD(D5+RD~LFf9F<#`#!z9 zHNA5kIbVTv@Nu^XkKdh-tNYWAGAjYE7VD1_-x@8~oicbF0>+IvCR^K(z*?b&kdE!A zODSBC>Is=cq`&lhA~bI<37;Xq{fvgYX;}8-Dk9+^G!#_tyLD!3|H^=Hsij)Yp{3Mx z{MEn|E_hP09t@dCNq2u(md}eGYpj)Bc5X9#thw~^{4b6eBC%X_M z>bPQq9XkBJJH4!5nDyrnLOyt~woSrlhplB5+f(}=_IqPGR^z)Q;f2e&Vm!}}lZN$T&)(A{8J0hA z7zQU54Vc;D*9-7`))Dhm$-n(-A^c{4_D2GhAv7n6>ojD49YK66A*Ks@xGl|6154Mw zti(U#_MZIGa^C*9vK0!vapCO^zw5iSZ<^cuO0D@QiH=_JwxsvT^#1gjnvfh z1R7mI&}AoF|IKP9SYBv(-X7+jc=v5`)9*?WO-!{oh&mgpMBCDR_098nyy`7L;Gtw) zSgvRS51*GgrZi+~dyQ+B7+PrwLrKuWz<^tzNIn#T1~IRiB9B!(kd83IEqUKWJV1n? zRBvC(JUH`h@MHD!vFb4ngl!%eg#^1k`D*2TKGThcU8v-4y|~f6I4HcH87bJxR0}6 zbz9nFF#P&HD&#q2Qxvz~Wmk>E*>3$or0DeBWGg?abAI17dc|tHtt7C3v0iasI@vsL z5676aN995fOsOf2mQicO^JjGBEX(gDYJbPm>}9#MVt;<>)tlP0uHThQSs{Lz&{$CD zc)K>@H$OLJRTYQGPfxOWOjZ!yQ`Z{*H6yOO<)Gn8w}&Q0UAOoa=KrG6c3evPJb^KI za^89E%jZ-6`|ebvJGWwxmj~xLwo2vQnhpNW|3whQl*7pCC<=v@Aj==>4{P?mrS;i~in0ED)L#C)u`-A$Pi3 zF?f33`A9CvOB>FZ)Oe&GqgV_ z^C{!Sruz49;b=(b5D{{Qv}(QEIKki}&Ecl8YIUsvbM;^F6eZOjCz9Tw?~+NlXDhVc z4~r#_c}5be)yjpH<-ppO74e(+u3zb`UdO5H(3>?d3OH$HCV7pwWIpd^C!x1oOPgfR z_U{3`p&7_4F*5SVQWSD274Udp@Qs$hKF?Y!%z{p?bDjZ(q838WkKgW&jt8f%JW$4? z^iZp@G$O5JzLO(m#0Y|7CNZVE)uVj3)S9+Xc8`?H0_>ESIlS<`Tuue}Nhe4?k1Tc? z*X_?Op%8u00=!Iu>^;oORxcx5*8qLEL6(7>6I}0SD~;~!ir%lc z_jcp1gsIEoK)L8*!_Bk^O4&+}8H@36t(Q+{ZXnQ+dCdJbN@vmVZs_H7B! z3@YdwBKl5N)7?y%js0z_tK0eO;!5&()?F|0+fpOToBJs*{9&wV`}BRO%@l46=p(q? zy)?0f65=>K=tb(@++_7q-{`uun$r%QK;yFke6H}rTUS5%mg7J5wX-wFo0^!$!vAt# z=(in>Wt)3u_f0AB)&u!#?OHOuUKHmv;T?$1`{caqjoW0Z&h7Q-_|h}6pKBcdCfug& zGIZsnu`PLJz`b#WY~6cq&Ck1R_1n5rnZs9V>-USs?o63`78U0g2HibzEfjKPHgX-d zz6g<2R}dXjm$~g?d7YLaD0FWWKb4T;bQgretfgmctF`)tspE;-%(;$1k9S>H&(e9l zXyvjJ<6$K5=UlJ$s|Ilt;eZpa(#`eMd;qx@N(r&t57~E@lp8}aHYr~+uTRyJ3IEJ? zqPxY>9JkE#!ae=|3D{5~fl;=kqZ);1m!;*B7vJXYCepZnV1lw+D#B~2S%%dHWSmv3f&wxyZZ zY&;Oq^82dHa&z?8?A^8)SU;L~f@9g$a_!C0ZsV5*ZPiD`xs$q?w zH;#SY%R)pC7gUJ7xIlFQUwmQ4>`+2FGIJ@Yx#)!VG$oKz@9=i3bb73pT?Zj`bvF@; zVbx+RDcWIU(`G+DK4?GQx~#2CU5in6FK!(lf!2MIRzIQy59kdkUGpJtkPT#zscQZ# z*(Nbv8y|~vJ?-TI#t=)^__+GEv0hM9GQ415ApV6ii#L@(WGe(?vkWFt`ysm$>Jq$DM;hV_zg=BBK%pz{rE?pRFzt#SlO z=aj`{|0Ns9APFle^%&EhYfr*+BB!o=9Gj`E#>V5P>tY2(?NnQ=DAl7ho9IM!gmNgu zY>&igF!=(ZjYwVON6u8^mtA<<9GK6#^8_!8nJ1OKkM;FS#n-=mbCINw2^+eeew+i! zw;`a6Bmgj&PrR{)H~6lA5cUNQ;A&iA31-)R?rwwYvF6a6*h9jwa4aHd2H!%2XhR6r zLS|b!W)S1b-$kE{#J;NIl6BgfgyhM#oH&OQqD3&gTv4wcl_Av{|03?&AHS!%98j1Z z4(K#hecWKmxM5r;17iC6t_C8O&jGY>TD7Fm^vC#vI!EW)x9sQu=C6Lf{ESaJhJ5ln z6FDj`A!@Dx@6Tm#LhdBju!90|y4z2c@H7Y*lx?-5mGt9j6uH}#PKXsTu3=yXGYSau zu$CuPlj17sAuup_+f#jr11&8adKj(h30XXw;5jl8UNMnH#Ks|~|GM~7rOeT^{qx;v zka5fKn&-3F%HuSP{KN$hhX?yZ{a;Octfu!VvtR%=W4e-1w|HqGG^!G?`Xs|AYT~EQ%Jo+maZ}FQ&)j~!A>cPZ2flj}E zOzH5Bh)4D7k;{Tu~ixz_5^ zMLNBp(UeP3k?R&x2))%9Pn0BPLqBdf*XjF_&_2zaO6{j4@eW$!pO_JpvFvkX5q0B< zc?i=ZU=3JNVW?n`@{!^>alNZ2B1dHx@us&exjZIx7hpB+ zIGZK!M=-F3pN42(dL+iJm|P$OCH%g}C;n)U#8z=p;H2Uh4yOD(rwYkuEZkQd)$wPa zfYg7pJA4naBm`n%)f>fRMns+9ECp0heF#y{LbVm~mirvFM3^UAGBjNI-1mJtN>DAz zrJX-%7YXDz?Idw0Q$)%<#0yh2&Cx(3FZXmZueBzEkRQ&x`bB{ol%<531HtX&=i7_T z7lE;W-G1IU(Je#w$|?1(W|jv(l+pTxXpU8Z3{%C%+;7`MTIbVl2G^8NE#dSCjCG-jJrAoLuuO)de6x;dc+{|}McfT>`I> zwGSj{jIy8AJ#-JA`dS GqO9j!gi?q_bw0^SH}5UB8S=;O6k^7;_5hKcf4nra8U!Q@n0@>FS`@K7zX&_rRATVF2+n;w)s3L@h zJkdIX=C#(1@eTh3$Si$*=JJ1eza-#|I8UK%d!4{{qSO!BBaC(~@a74p4HU`S-xmn}ywpc=6{} zSI@&z8P!{MvP7bTp&b8UpSQ>Wb?a>NM+Az>_*Vek% zuUA71D;T5+3MY%@#^N5-2aBlvc2C#nJ1PiB9z*g&=KK|H6!l$I&!7)Qe45DDR>)87 zLT`G`?)|~&6~0Gn`fZz7sNXe?IbtWz;y^cq9vr3YB^vvsImyXnfrT{x(PBALpDJw20JR=MupH?hU=hnsjg%~V>MJeG4TO==8 zNMjY~7z~13x=5Lbvaz)OI;zK6P=9E>jnR(dA^HB5j|Eb{2MgUR9VeU6kvXq z6(es>JD?v^gbI!hHCPdDD@B%90+z5s6+EB6KWXvpA+?7_EOL6`5S8ZJ{$wr68qe2^ z_4iK!&FJ3%Ly(SXUCl_O{%!Vy`?AE~5BlNVJl7dL+*bBkV81WoG#0s5Qo637GNuQ7 z_f-Xk-j3w75pq??!WJN4&tpT4K}-5L^~zU4pI16>VRgyAk-V1LO;(C=KV=0h>$8dX zy4ACIk@$>(Ko}qU_-&{oaJ|uP;uvv#PvPkarQL|AR}yjE%BkI!@JJ?z*ziBmhD5mu z!inNYV>xc5N_->w(n5lc6{OrZ6Lx5y8~h!=F~;MJ9j#d3sAEm)Bw-qSm-jDMaX$ww5nB5zfT0;CZ#fshv0ZJO3t zPyj*nb8=|W7Gprd;#__2UUb+|4XetVFSNJ8r>kA7YXhG56-`D8Iq1-_R0`jpw6PvZ z{M)>HjoS$phC6Bz&e7;jhi}j4NR!iQNi-$EPxwc5cA=Q}n-5L?|MLG5#|Y!|x`G{p&^ znO2@%5-XNj;`le_(d)gNPH7}+&mfo1crNyf?2W1Jv{}=7>!0LF$zS`7GSvxG7@rA$UcywRbxGo5%hf`A-9N{ATpspKT8Hl(5zbRE@vzrP!55n zJ;`Nv+>Y$0vH6SgjwF@KR~Z%askJA;$0uxAxhLnd_rF2u2V8SMFY1gx&YH~XSI74m z@|-QnaIge(@>b)rg0ZqiYG@E0ddW=3ia&V~K7aa~sLdd(#wfI3pJ<=T=1dKRTqTS6 z0E0K=eCVxsb?Ic?fyeUK(~VJ4(GaMw^{3Hdk`M-&Y!Eo2Qe;PKS=V243rHwDL8#r} znl)@)Q8`RvXe^#N*lynuqC6VUsIBFf8K=)N_pv%B(MFZ_K7XBa(7P*nwPEGg{fSSfE+Ol$JmH9(JU7JtJEb~pWztO z;D|_ndF7JEYXeu8jF(!1c71~0qJS2@2QZ|5pLw0xa8I>=fqOj$T@d%D*rII zDONJiIEi^)yWE_Hvz=r;IrCie^OCjXxos_-X2N;nwDTS#H>Ugaxy6=1f18F&juUFToH$8}@{%`%pgT4_ord7uOO_YRvf*lBJEGnJ~~T0VAj- z`*KeHS!E(Nh|(>Yc5Tzc4%UW)H#|oki|D&O^^hd>Hu~M+O`#g0+sb8>H5 z`+mpK;(j34`(=QD>43?IhxFlAyg_mW2687WzO}@8j*B7kT?I|%n{o{S(tf*YwxhPC z_x7yYU1;yS+gI90M(Ym3IPImVQDf5c@Sk(A;&jnohLGJog=_hcD4VKK>I4#M=!|nC*QH(mAc#<{sCh8}Fd#@UB zs##K+r{-ea!uHxnzhOaxW23P+m78J%!-#lg2mL?mEyw4F4S0w2P|D?eOql+yJ{xX{ z;;Hk~@0YDsgtQtnm0FtJ@eR}J>iCoty0E5QZ=(V7(~J&zX+kdX6m-$=W~2^QJ7j$0 zfkj0MbJMfTbr)PMolf+-3onuoB|8&1#XZ<9(+y)awu07+I(YZcAnyhOfN~WrcgAh6 zd~6c}EYSKr6v#?kRvB*mE-u)ek)%ftuEO%?GcI)Tg7dsQuv*Sxu=hMUV^*7mQ^YU( z3vb$r$KSTNBa9~fcD~(u>%MW8q04O|Ye&~?GOK=w$?Mw)LVbtxT43pSkX_;zzvGam znan!#kH2aa?k6J7Wi!9v!fz$*Q42{-z8)2H-=8-hvoFcAHGL`%u$z!u^UCJ+Q^`c7 zGHbf{p>$G7g~TUsqEaT;xjikpShv{pCP`We{WyPu>+21lQ_IK6Bg;4;xWLKw-?=S! zJ(G1F-Bq6~^Twui>a9O>YUtr)bM&V}{SS&p^3|SAgJr-|jt1n}dFgga_ve3%v|TyS zFi@~N*Uv$G=t1^<=wUZB(E52Ja-vj6A4)-z#fKzL>(DBk7QC+}dn>c3Ls2{OhXgQ$ z;1MU_gCUIPBIy3Kyj*~%zkB!Yf^Zk{T3tQ+Qes)eEw44pogu2^IC<=1;-bW&t4W6> z(~hQu5W9FTKV=d^lHZW_Ik&=5^xdvTDzY~^F9^%b)E96oOr1VXA1_$d2nO?2U5P;n zB8kNCcRruhI{~tsp1}P)(|Dfqdm?CZuraY4Zoe)nGJC_NleIu+X*U3I3nb?TyqIpJ9TOCsvG8%#bZN5%^S)F$kk=?NkPa}n^9qpkO*nx?Tz@`24WqO3 zQPq-ir0WaD?wWMI?hi46btP-LyhsTPL>136rtl`d{w^mZSa|_JH+bgP({nmQ1$+Ts z;-JKTw&c9thXm}SSdg&q2z{zE^aDLh)9J(9`n;|J%jTP)t(1AD&B{yL_VK-aV#&id z6ACGAHTuQlZI*dZI=V9Y^xn72b$G|saA-JsC_!`np36;U+#Q}T9g@^V*-i9&cosjm z#C^6$Jt>_@p_iqP*5X|)RF%@P!}9Pm2xfYbC-M5=x_r% z?AJayMTX`qjP1kvO_;|XGT$>DX8UUiAt6eZAs4it;Eje3g^$U{1&i7;WJ|)oB%Lu%+0>-2xW4s`-;qAZNBO{!AVk4dgu(uxh z#s|f9+n@aI=kJIoKP^B`$YPT!m^1IZ|1Cg5iTyRy^E{%}4Le+6LC@uzpQXZ^G1T{) zXRqqEf6uZKlvFKWnY-x7o>>}mQJ!SNJ(neNPK^E=aINC~;Zl^^I`DO{{LHE~LEBJaQ0u&o)=5iAbv`wM+ zP7l7xN|k6Er#@53;zk1OyN@i656kjY`gwXRRaxqmKC*NK!F{e4{Ti}nY)_Ucg0DI! z{m*HjXbJA<2MQn?aR)qQHX5l$R)GGkvRz2`AYmzd7DJr11dabEQN%(&qQ;Ha{siiS zVbLVR{vW<>?{NK+V4^ilWQhv~Bjl~l?q+!bbCRRQ5gV$$hJW*xUgdJzb zlww~@owE{dav783Y8v+n5t99hX!y#%c-++p5aWcl3VOyY8h_f&h08 zVkYS{28mO0IO->HL)zWeF2R*~_6Sp2eR+U9eEM`W5s77Iw9jY}ZL4iXtOmyX~vsMW9; zeR3aC|4dS))X@rfZv>2x`<=*O+l>l@hhL_(@B7A5)=_R{`HbWYb!W6z+k8fQu{7}0 ze1ez`l?8&)YmHt9Ly(|RtYt9Qj*RJhG;|_(dR2#vGCd7(Ppk1BA9x^f$oW_wV14vh z&B7A&u}NyF3|LgoTukZ7u*#VfJygxV<7bOwX>L~iw9bBbS2+092{u`U%U=8TKJu6A z)PxzqWuO8Y!Iw9gXRXr_wyE=qr{Ufl9#&)@)k`x#hlzDbeH{$w#rFPn@;^4E?`*I$ zq`#Iz9NVOibpVvPh}`e52E;9zo6Vzs-zvGUOg>+I8st92P8nqRb16E#e%XnWnuY0z za#Em-?KI`3K!niMltNi!36cKg4T50 zv4^OAc}>us*_45oZl$OEz@mkpt27EDri3%|=yGy^f8nu}u-aR0PqJCMei#;%Sypx*bzpt z4f}o3dP!f>3n?zBr?(gzGT<03H)v(MY%XfMm6=t~9IHjPsKGWWP)Wwy{@BlgZC%qEf($Tfg-??n-CEQ{e<$ESqHvvXcp>jAI_3s$ul8< zD~j76&^`|yfa6^RRn_>^@0uzMRcy4MGV;f>WYmCb>RKmLnXY`))aTH%d3kacGGjp5 zvlIy_tsGcVOF2>>!rK>Kj9k<}PC;rX^mglFl%Q4_9AO7VeG9Pau0#|pCyh`~$C zh|F{}GA%sQRq@e5Y}7|yE&U)N`ie|db=lX$O{!zfI}ZqElOgxJH$u=ea$`@pbc{*# zRj)&GhY zJ%!goR0g9Qfjp29;6mW3Xvb@WcY$4GMyMVTMM_8JLIreR8Wo@IxEg}08c9N)6l>B2 zLQd_)Sw-!dKU7SwA(WK{~&c&s_^!%DN6Ep@~sGo(BlF{GZHhXBWI zjTnZeG)T*i3nc($c%e!>W-%^c^5Svi$!Q0Fk4OrOYa1YmTq~8W1fgU!I-A&dZB;TF zWz4Zyp&gJYf*KS$A*B?&vvQi)gl2z`%PZC&zS{RZ1o0gre60bxGeOaW{6woT9qZ*W zuj(s&dfsbMUx!a|qAx)?rYJVp!%s8C=E2PGCS(PddRL~B*b=J3GmAs7g^8X}6TSnd z95Sj#3z7j9A!CZ0O+;elOoPJUVVsWQ1p z>jqnT0!wKM145`hRD#f6-ZoI>K?vZyo`AM~$d}Gkuh1HoH*zLFArHuvE7;M5OZtiI zOCnB64yO2_n`DopB`$kJ>z{@Eq1!6hPdi2YlF(3%sHmjvNaQeSB^V#h$2XeB2kV|z zn#g`26D|~w(g=&~NdztuktsxRojeQB&VuPn<_Ed6TcQv(LJwgarM9p*l%&Gv9f^@v z(rl5;p^Q+>v+Kt!Up2u75h_31d+uR{P3mzBv_;zapc_mYv=SO93{sYcx8O1W1s6Nt zI(wY&^PU$iZUSm34wMrm=k7mg8 z*r7%)Drd#$qpyoSoiq#-7m9zn;PC{I$$y&1aX}{H9nHdM@Y3=)v;j#P5-cUv^ZH{m zI4~Z$u~>L$tEo81o?>d;BFdwt(7PW%RdB~YHLgk>LwNbdS73ru+&)v4{84u8dqb&8#->zxuQ{tjsD`ScM-yk^4Q{~NA&B5E#{A7^~tE7j1@ zPzncI=sDGu# zA}F`9)Dj3_SmO*IDuVL>A4|!c1j6Ky2YQ`MFj8OX6rZ zpfOf2hzdh6HzkXrT?gI>XFSvwRgq3G`AMaLoqv)t#f?8E#!-%gEKA7{a@LUTl|(cZ zQfy?Ms2~zUtPczy+^67wzEij#0icZ`jv@8=K+cGLCp{;U1#cvFW*Swcm|qCUh?AI$ zbtHuPseT>8g$OH-ZWRN$!Il(dip-Wb6d1>$$vo1@@tAUah8NbXg|aBv9H7Z ztsDsf=mAB{h1NTIGJMRAm88M+3jVFZKEhF36Bwvq&W`+Za6>@{zA67Vj|`P!_6>iN zChjah`^0i-A>=aPe6}Z;z!Z~eh$2fp(t3UNk<53xzM45maji zj1JNcV0}LR>1*K|1dm38EbArq^KSgao$Rml*^*AN^N~FbQgJebib#z(sYKcx78}X- zDCIx|FPia9u?bJS8=1R9M`P=dF=Rl(z}Sg6!F+fyO{9YlnDFiNp_qM>`>0<1f?oYn zJwn%mCt%YbFXQHaXwULE6jmqyJ`>F3^8jYjx0}fTEx z8wtq{pF*@es%itLHK>0BDbzufLX^Bg=6GFEdJ5TWE05?6w^XHis6T}oS#caG8VRwf zju*?o(bQYYD;pjX=`QK`sf9v$F*TZJq`~Q`mNYs=U$2L$>BLpr>{>z*(!=c)Ohw5B zsx0t9MR;WeHLF~m=)1fF{cK-8K-#gXa^9&oxndNPy?7ryQtfS!IovaDEl1vQ{X|gM zu8N$xhmAPo8p(hxLwVKJ{FIpcWU9ribtIllFn?ft=8%1%fWKm9aGh$O#fbklcn2#6 zd37pFsEG9N*JTmB?Jf-$6T{mWju$_$r6#kU9_fFT++TBPE@9Bv*57u+uB8^l-HhlF zKBWgo#OF8~NX8dQepD8C>)76qz9n_)vR*6kdcs1`j6^6Xle3djyiy$RRe*rtM)tcr z)pffUk{n5*jV@xFBI1Y5&u>ok!1>)70WBCr8fPk69jg3m3Z`^lN0ci$w9*6V^H@|SIrGeL zJod+=#MR;wl_}}?dgYx(zHwl9xwpx`?4^+Qf&q;QK{Wvl6-H5X`GIIlQVIM0e}L)D zK%OHu{ak@en8y!w17MP6cNU383nO_+_yRrz2Vm&7ndUbc2fn(iuHRF+FbGKHAY1gMshO0$ea09nYpSN?V2v;(_{tL5>ncBNGM|*~BVwv3RVH1s zxK^l0!z3{eXFu<@1uf4g(Vj3ylEf=JkTbI1DaSHK8u-|#A+wJ|SsE}hwwMLU?Uj@# z-U@dwDhQ{R)`8|bi^;6@V&&?6jjoHe`k-%TpPa@7cs$Y=lS?%_m z$(9zSOyBxwm-PG=$^BU2u@EV!{4FXJZE`Jaacp8b$dfN!JHrs{uv0$xb2e{^>dFb9 zF(RabZ-~aS^d%Awfb2d?T>u$e(B{4@K z10ZEvVd)g>-0+om(qjoTvaxFSQGx=7LT;xBH@oHsHS_*j>gUL}y1Fv>;TG4ucf1t$ znac2^lmXl49G1Ys=OvkR-6l+T#CsdK@m#)*W^M%}d|B~iBlwxFfwqCq4sdV3Rg%T2 zwU{|JeMEf!VnZe&IzK9&c3KFcOgErpw>2bvmoWHW zW1%r2Ajyqi)VcxiPq#6-eV6|F8dTvQTJxw;Q!*dQNmX9J$q90rMF=g#Grkff4O&kV zrHv`9UCVfS-sh4@w$Sxjr^?!=R^AqUgw0Hy)Vy>YJ9V^kDl|^N@tzNyg(YiH&;yb;a-ji?+CW#1nL{M@RJ%O zQ!#oo)MIN?GNrnZsQ~AmVmxw`ljY68wmx^N#q6wHA3X`hLwc?kFJ_dLepY#DhI1&@ zT$F~qq4$j&kP-Vt6Pw`C8IL7ctbfaoA*iZA4gr{r`O|)pqViL5fZiaEr*7};*LC(b z*{t<2I{0t|K!KsLn1@+lasH3#Q#xa--gh}{R2&Qc%_>E38 z#0vCp(Rek@PWzT2S$vnD3(Jc{Wy38LXMC7Zf^mq+tCSDlz&}xn|5*6hr@>fFP~_%D zDNVXsyVYEyf_R~xKqZtLc1#yy*$iNz1|`}#^lAw@A>~Kpe4kSq7;`|_hU+}ps^WRe1 z2Ko3Ci|L!fV?or4jHob~ac3@jDLi^TLuz_eQ;6R;8q`vC0uX8fGlVea5Pvg@Y6%1< z<|Z;Ij%6lifx}nh0Z)>-wyc8WGbR>zJ*cH!KC}_up;cvI71YV&13|Jur$5;+F6xtt z09g^6&P!p0e_F-AKjjW>&bCd+c~_`}4@&iEqvhliR1hM!FL5;7lI86|xgC?nno|@L zmF;zlBqS#EI={`@>My;SFR>Hf1Tq8!Khvg7RxsK>9f~|CL6?~nUp@+wTk2g+`rsVS z9O81D#$h`*#!sl&8Jej=t`pvO^Q`?T@^up1SC7{90WvBDNOvU& z3BGrJEDD1HlHGdZHYb6xRU!2p2FbTFPbD1Gh{vt6vcUmroej5kif)v;;?)ZwDH2@> zR~HHXANu?ogwkvEl&;xyrzN?=l48Dq}FS9B*#zlS#^ahJ* z8fY|F_n6@bmr7s?*^PE>&ZidGQKDc*H&2GkYqe`So^)}8^ptyi66x-)*fUBzTYr1%J z$mThnO=s}f{QZm-I}SsELzi?58rliZJIVnImve@evg4gp(Nx3(NVwMcxeTm!CC+(LqnpvFL>{mzDz-+!audJ$q<-zXME+oyKQh|t_^%Y zIa6pABO!0`c|qabc((8yjqUI4r1Mvgt_csk2^dyPkUVzzPtTSfQPPjPzgz|S%`$iK zI2_Ox7#FZ(FgK^w(>0biu+Fvwh&4XwDyY2h!6>8N=UV$<%uyEkF5Ow$l-8d$`{;=X zty~gDle$R`2YN@SHz4p%B3D(w8PY^bfp{F?=_YK#&a z7M>;W7D73oh)E^$P2?EBH=>+iCk(^H%Kr82S+kkOvD_boSqK?ydR>Rd-(b^a$paa} z6D9&=@onR^R(=kz!U% zlv%`TLm?&NP?MAehED>EkbXhIdYT$qIMJZKS&*aW$%D#MQ4*?!q;UHFE*W2bT* zw)e=<=hGFw`n=kbW83X%2;L0WpV)F&d+QM~?ht=~Ry&qyPr{WK;FsJLP9kA-)|Z%+ zw6XYSQ_Lk`xH$H;HOYUZ2*-zXJwYy20dy@tOh{E^rls(T@Sg7t>>gAY1QvGPiZ_po92fk;e$&J~`;S}j(7^}B@BjJX)Bgb1>+hbGeT3`%=TCng^q~#^ zfW@qG`O|&!k@EjF1WbVJA8O9*kIp9k&nJ){>RYhCUVV4`uh;(vG0D0xm71dK(ei&k z(eYq=18l!?Fa7U{{4)|29!ynH;TbUhpHJkoz{@v7SJMvU{?7^j=aTpg68S(_t%N%+ zH4Y_zntAcJT>sfm5-gvIU(wZAc?vb0y6NY=MIQX$HzU%D6JXz9D zA*P>`S8{Aclv5R}i1Q=BI>=x>nVW>}KP7uGF7Czv9&`b`QD|i;RK{iF@XR$o(X_CK zb&Cq;VYG-8;ER6OeO$Mm2PXRb`H1|1^`}8VNVxE!_=XKjc3OP&$_MbWy+x> zdN5w)V(I3*{Eu}D1+y$n+J}m+4#U3t6os@|U_s=VOgJD-hy2@defXe%tY8}`*%#O) zKPo!e>k2y+z$bpOc8TpjoVNQ2;>NK1(S76d_%0(~*By#?!7j8hOh|G(ai4#NiT(Tw z^oFtmYM!)zVevVQu2lLSwAx+dVrkjl(Vs*+BhvhkgS4Z9u?a60pVttK2l5f&I~fKQ z-NW*v^Qe-gsZy^u|M+PzVv2!~ih(Up{1%WyzIe#`82{9AoJy^&>lYZ4uE2;ovUd&_ z2!c_h+YY1%9{c27knf8l6GKp3T*8nb13ID^HIK*ns-kde@VKcDRzD#`I7Uih!l{3= zOtG(8uSRvDv`Sm&c8$2mr5JlB=VGVNLqd^%2tM024Y0hPr<}j#IUslY? zq+nF+%9kkL21EYIV<|?$1bf5c64nm_w)}8#!wg#8Ci|K?X_bN3JuIK0eXC)AK)o;# zM+PhUYe-YE2W~K?G9k$tzvig6$)~*JvW!3+R)SlD3HR>H@1`&&H1U6?)ir z6%gF+WZqH-MRiq^1^a8Z&+C>1{NFM{gI&}9sET|(c(5u9rwk=S=8GCAmC4>-wTwU* zG}2flrzkS6e)(~(u(0OGh9lJDp;`{g3&?#LQTBe@V9LWHQFLYkgJn{OJyCj{F@_nk zE<5HizQztV&qeYJYo7?RnQ#3S{j>0|?+OF?VEP10A$bkUd>sbkir>f{G$`7p{}e5z z7_*3RQ>qG{K{@D!@|l;h@3`J^Ss%I1IlwKRoS>-P!uE2Qjnq7V-HGewZ~Bl2K8X*$ z`P4Th8i?C6o?QqTH-?-B${L!-%bb*VWtFSrC?~IRajPwqYSpf`B-a?)cFm9LOfP>( zx_)(W4a-5c1w7~hl!yj{QP}!0i2$;TUb}U7bg`!xUt4l{8-1CcE(rNChDVj>Ci;3{ zriJDQLPRo#s7fS4m>*Q88Y;SBs%UmiUPq+zkBGN{8m2HXl(M^Af)#TwW=S)7v|~85 z=&n>@S-*M5&T)(i%(4_#gg|8tyds0z46;-zEw`Ve>K~0@i{liO{pM5B)azRipdOCwwI(YvJ)6 zuiQuiUC1^8Bo^s^Rm=+ZgW^O$zkjHCQ$dH5!yQ!79QKov7H$?Ur5JxLowcMKH8V); zQd*t=y6orKGdX-Y`M7@Mk)D0MIJqK|H&uik1Q84kx8SdPf3>x)HQ>jEY7IcCyf%Bo zKi4C<-&+ealUcgu;?N;+M{F{$bEY%{2ba>tuJ0!d>;$EJQTg{{fBc@UHAbfW4 zfq{YWMlq2>6S_!?y0sn#r}VwKRTe0?GYb*ijAk1Z&x$EYrpm)6oRP-EjnAy)gw8C` zP)86f{S3Muw8MktPiBD^SrfNyzVyUtK?n;F@Y1!13C^fk@h6=Uo?;dsX`>q4ZrWL&QiGNm~V zz**CK5%bNPk{XYF{A(sC2MWTSSO*I$BPJ;OlWj(5zfvv??9g@!(`b*|ulpJpY#qZJ zBN%Gozl|}F_MX|wh%Do$UQZZH0#&-Bc+`^pqFSBxe9g))aerAp3(v$j#s#MPVTHe_ zCnK#HqkoJzGWjRz&23t+N%n6=w#&D)+3Fef2q%QQVFt}4$9c%cd2#*bt!5LcUK01n zP<<3>-tTAqo7;ugFaOAuEDGFrpW_qE+gRy02(w3!gTWKfT6REy=+`8K0DYat_aB#B z?$Oj$NO1V^nG&?bH@ux^f4%4rXLa_Vi!;vu#B1@0H&_COi#cH1)4;VRZ>- z^o6`VTo(N(imUd^OQ)r-f8YCw+OLI|vgX$eVaRngy6~&9{vXCE)Gbeqb4>33<%>2H z0>fRU12Qi;#lrHyDrix@ne5p5$1razQ)o%>g8o8#5Vbk&=;NT-GlsIm32oEpmT}84 zH1dJ3g?p!Ak9A^pM-PVK$MVyP2hT#61Hi#5u(-}L25Vf;cj>dLASh(Py(G~4w!xAh z%!8UCfiM?a7xjlOcQQc$Tdl8aT@|x-=kJcc=KTpU{<}&$Z*y=w$DOXGRP+XjHU{6{ zx~Tfbdz&kbU8vJ5`^EeRHFb4Fgd}4w(-(JZ6)i>#9uFK$v|`)g=S3@&dOOOapiUYRd{{wf$rxuZC$ZYcM%tn>-4UojUbYA&bm zd4O?b0;HUJFWC;7R-nJU3G2B^|GxTB3GkQ$NN-u4(26h^3Aa3RxFc#bkQs=ugN3oh z>0{kWzsu7?U*p6mEM)AW6krKzzX;Gzo7XqX6rbEEc4AEarYvw!Y0APlu^4KPGpB%u zG)wSZb^fne3uLyQOO6%^5u{2*y))`oAaS+oxPX?A+uhE zR5i7_Y+@Axs%p30Wlk5l0`U$EGB6L*lq1K-Xsarn)C#MP!MEO|QOziKe_Rqn58z=( zNJ!FzyM%4J=tmdOL9!{`Q@E$|9nN4%Atq@KF8b2s-XbPCCN?sP3*K;Zdns-Z+aD^U#KQ}XZPw%9EOr(Z={F>P=xTh_^peeZEC4XX?YlT!={7kls4 zE}36c!dl4ewLi11zH&%qh)@sXfT7s)f$4TH%@NI;uvh;$dn=_6&Zl~?fZu~V;{-51 zrc}z0%`UPYuS!H4C#`hv6};O5RcGF9A;9`HMV0=7Od#TC#eBqUUr6z4zu7h6{w<+YwM!dX=ks^;LNL;OgSw%Gn5fzb_^B_ z4rsWUmaRp-1shRt(g%9_s6x-bs2W&9*_SYwnDOyfNOYwk#%->-n~BG;l$XzV2XQ|x)W@p$tnQWwHUsG(y!Asg`xBB1n2Vw&W)Uk}zo5p>4 zXHx3*zMwBWORHO~D|0XVWjb?x9X7N0s%^)lLwTD+#rV#0M4(2S0B6JdkLv&nL&(}> zDWpBjKVKiSszZagW#HpxRPznh)TB$w1~nH;x9d#jMRlqy>W1Ii)->@YVrf2)mO!cfv=TgXz*L3-NRgVa>| zr$6Wt2oiSxaz#%F%-dN53{*b?8b|@)JMz?EbOoVW&f7QmMHW?rHM%?>XV#Of?UiZF zo83HR(TKiWL5ta-&N)aGGBos2!&L^I-8cW_nP7BbkYiZui#6Ke1jqd;EK zk8lt}_#Km+y%U6FIIPxtOMt7R?tBp6K(aHECg_gN`ovP#&f)e?(a40pi${6;gB&d-_UhMInlxd?cpCM4vLfA)aDCa%fWMTtH9TalzA3s% zJdQT<9NtLjxF(yUBtocw_>}lxE3O5KR+oXMpqd4;gw&|X^D7U4CAzf|$eKMmAFaRi zLqiGaC(~h$5B}(iH87m8hz1hDvzX%b?XQoDF9dI*LMPu4P9i?K{jdaWjbza;osPZV znqANTc@xPqpGth*nb7rK+Og3;L4WcN@KVRD_~MD}eV%Vy<>ZcRo`ilBO30HV=wIg* zHrPK+c>jL1nUdLY1w5mEO@(qLo1;su&lGX9Qu}BmKl!)K$*iKsJSra8^LBR_hZn~8*ork|_=@7Sg#Is&M9+Yw zM3nDD+?-7-(KiTxHQ|3G8(}dAH-O5oFBw|ZRcpwU@$x4jbJ9r5Ve0={$p^Av(O@`* zv_#3z##r`^S)SP{)z*gQ6Qvo8y)(}>F<1>zPf*Gg zFbk!AkB}J;{Y`7^UoLpCLjPhO%0OY>7}oE_84;wsDk@K>dV@A+Y%{*wL`q-y_y{q*ce+dI+L2Sj zfwkL@<~z^Thc!TnYBU)C|86WM#4tM)$ppm7YQ$C*ViH5XaV0s3MMAQt=@D_X9&&Ph z5g^{?*{rMC{M+j91pbSal0fk0VEz%vRJg=I*SgfV+wpa?ZvF_#-nYvJZv$6= z?%GC&$-c&;@cu8>g<=GUL|3c1P|UN&#`C@au^-=FHj!VHyfo+I`na}X6EYZj5#ii& zne6Q?@G#Qa>QD1OII(}P=>thnC4dAuwFD;Yk_>_6`QYTs+hYgS9?hFkX9hvRDuKNV zN27;ursKz+hQt5N(?9HM11gAyn#GvSwDfhCo8-^1i(k$5;C$T|RDuNp6~|GvLV{du zt>^UH&7UL1d2bI)A5!k7o&o+DuLLZTFzI9mGQ%I& z-DZiill@8d989-$Sj>1Y9a{gDBmQqT@o%#*4JgKoBQQ~`y={(hTQf1)V`df_;a7sE z60B(UGKpb;{X>Yme9~ega%i))H|oLa^bR=uiID#XMIyj1K&O=by0x6Zq8p!J)zJ6| zK_l}WF&0a)WGMnx65i(c=kfFIo52&y%-G|oIw3y3TbJgmz|q4pcCy3!r{rAMT9ByN R`zNqJX>oZ-xu~J<{{zof>IDD* literal 0 HcmV?d00001 From 8c8dbf3fab5c4bef74b2cc1a262c40408d83d63d Mon Sep 17 00:00:00 2001 From: Niall Brennan Date: Tue, 17 Jan 2023 17:16:03 +0000 Subject: [PATCH 35/74] add correct actions slug (#4060) Co-authored-by: Niall Brennan --- src/connections/destinations/catalog/customer-io/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/connections/destinations/catalog/customer-io/index.md b/src/connections/destinations/catalog/customer-io/index.md index 7bfb0ccf6a..dc1516eb71 100644 --- a/src/connections/destinations/catalog/customer-io/index.md +++ b/src/connections/destinations/catalog/customer-io/index.md @@ -5,6 +5,7 @@ redirect_from: "/connections/destinations/catalog/customer.io/" hide-personas-partial: true maintenance: true id: 54521fd525e721e32a72eea8 +actions-slug: "customer-io-actions" --- [Customer.io](https://customer.io/) helps you send automated email, push, SMS, and webhooks based on your customers' activities in your app or product. It makes conversion tracking, optimization and re-marketing easier. The `analytics.js` Customer.io Destination is open-source. You can browse the code [on GitHub](https://github.com/segmentio/analytics.js-integrations/tree/master/integrations/customerio). From 5b5eab3b581f0d72eb38081ec7c518478ec81e40 Mon Sep 17 00:00:00 2001 From: Niall Brennan Date: Tue, 17 Jan 2023 17:17:33 +0000 Subject: [PATCH 36/74] Add platform code for setting writekey (#4050) * add platform code * add platform code * Update src/connections/sources/catalog/libraries/mobile/react-native/index.md Co-authored-by: markzegarelli * Update src/connections/sources/catalog/libraries/mobile/react-native/index.md Co-authored-by: markzegarelli Co-authored-by: Niall Brennan Co-authored-by: markzegarelli --- .../catalog/libraries/mobile/react-native/index.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/connections/sources/catalog/libraries/mobile/react-native/index.md b/src/connections/sources/catalog/libraries/mobile/react-native/index.md index 0f73842b25..04213103d1 100644 --- a/src/connections/sources/catalog/libraries/mobile/react-native/index.md +++ b/src/connections/sources/catalog/libraries/mobile/react-native/index.md @@ -595,7 +595,19 @@ No, only the plugins listed above are supported in device-mode for Analytics Rea ### Will I still see device-mode integrations listed as `false` in the integrations object? When you successfully package a plugin in device-mode, you won't see the integration listed as `false` in the integrations object for a Segment event. This logic is packaged in the event metadata, and isn't surfaced in the Segment debugger. ### Why are my IDs not set in UUID format? -Due to [limitations](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/uuid.ts#L5){:target="_blank"} with the React Native bridge, Segment doesn't use UUID format for `anonymousId`s and `messageId`s in local development. These IDs will be set in UUID format for your live app. +Due to [limitations](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/uuid.ts#L5){:target="_blank"} with the React Native bridge, Segment doesn't use UUID format for `anonymousId` and `messageId` values in local development. These IDs will be set in UUID format for your live app. +### How do I set a distinct writeKey for iOS and android? +You can set different writeKeys for iOS and Android. This is helpful if you want to send data to different destinations based on the client side platform. To set different writeKeys, you can dynamically set the writeKey when you initialize the Segment client: +```js +import {Platform} from 'react-native'; +import { createClient } from '@segment/analytics-react-native'; + +const segmentWriteKey = Platform.iOS ? 'ios-writekey' : 'android-writekey'; + +const segmentClient = createClient({ + writeKey: segmentWriteKey +}); +``` ## Changelog [View the Analytics React Native 2.0 changelog on GitHub](https://github.com/segmentio/analytics-react-native/releases){:target="_blank"}. From 9560202801526b90d52b24bd8f581c90940d7bdf Mon Sep 17 00:00:00 2001 From: Spencer Attick <23665784+spencerattick@users.noreply.github.com> Date: Tue, 17 Jan 2023 12:19:28 -0500 Subject: [PATCH 37/74] add info about how data comes from Clearbit (#4040) * add info about how data comes from Clearbit Customers in the past have been confused about dataflow with Clearbit and about why they see analytics-ruby events in their Debugger. This update clarifies that a bit. There are past Zendesk tickets that all confirm Clearbit data comes back to Segment using analytics-ruby. In addition, Clearbit published this line about it in one of their blog posts: https://clearbit.com/blog/enterprise-grade-analytics-for-startups-2#:~:text=Segment%E2%80%99s%20robust%20API%20and%20Ruby%20library%20enabled%20our%20engineering%20team%20to%20quickly%20build%20an%20event%20emitting%20service%20that%20sends%20usage%20and%20behavioral%20data%20from%20Clearbit%20applications%20and%20services%20into%20Segment%2C%20tracking%20everything%20from%20API%20usage%20and%20page%20views%20to%20signups%20and%20subscribe%20events.. * Update src/connections/destinations/catalog/clearbit-reveal/index.md Co-authored-by: markzegarelli --- src/connections/destinations/catalog/clearbit-reveal/index.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/connections/destinations/catalog/clearbit-reveal/index.md b/src/connections/destinations/catalog/clearbit-reveal/index.md index 76ec4c1d54..e28ecd6a16 100644 --- a/src/connections/destinations/catalog/clearbit-reveal/index.md +++ b/src/connections/destinations/catalog/clearbit-reveal/index.md @@ -22,6 +22,8 @@ Setup within Clearbit: To verify that the destination has been set up correctly, send a page event **that includes an IP address**, check the Debugger section of your Segment Source. Assuming everything is as it should be, you should start seeing Clearbit Reveal data populate in an `identify` event – click on the specific event you're interested in to see Clearbit Reveal traits. These traits will now be available to other Segment destinations in your account. Notice that all Clearbit Reveal traits are prefixed with `reveal_` to ensure they don't conflict with existing traits. Clearbit will also send a `track` event for 'enrichment_provider'. +When you make requests to Clearbit, Clearbit send events with its own data back to your Segment source server-side using Segment's analytics-ruby library. If you see unexpected traffic from analytics-ruby in your Debugger, that traffic represents the events that Clearbit sends back. + ## Page From 59f1907b4dede0ca187f932f2db1a256fa2f5e0e Mon Sep 17 00:00:00 2001 From: stayseesong Date: Tue, 17 Jan 2023 09:40:52 -0800 Subject: [PATCH 38/74] edits --- src/connections/sources/catalog/libraries/server/node/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/connections/sources/catalog/libraries/server/node/index.md b/src/connections/sources/catalog/libraries/server/node/index.md index fd56b77dfc..8c12861568 100644 --- a/src/connections/sources/catalog/libraries/server/node/index.md +++ b/src/connections/sources/catalog/libraries/server/node/index.md @@ -348,7 +348,7 @@ analytics.on('error', (err) => console.error(err)) ### Event emitter interface -The event emitter interface allows you to track events, such as `track` and `identify` calls, and it calls the function you provided with some arguments upon successful delivery. `error` emits on delivery error. See the complete list of emitted events in the [GitHub Node repository](https://github.com/segmentio/analytics-next/blob/master/packages/node/src/app/emitter.ts). +The event emitter interface allows you to track events, such as `track` and `identify` calls, and it calls the function you provided with some arguments upon successful delivery. `error` emits on delivery error. ```javascript analytics.on('error', (err) => console.error(err)) From c8d25adb914877ed71eb1aebe97a954c9c7af6a2 Mon Sep 17 00:00:00 2001 From: forstisabella <92472883+forstisabella@users.noreply.github.com> Date: Tue, 17 Jan 2023 12:41:36 -0500 Subject: [PATCH 39/74] Apply suggestions from code review --- src/protocols/faq.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/protocols/faq.md b/src/protocols/faq.md index ae0813f900..94c1f98787 100644 --- a/src/protocols/faq.md +++ b/src/protocols/faq.md @@ -13,15 +13,15 @@ You can subscribe to a variety of Protocols specific alerts through the workspac ### How can I get notified when someone makes a change to my tracking plan? -You can forward notifications from Protocols to a new Segment Source, which can then send them to notification tools such as Slack webhook. +You can forward notifications from Protocols to a new Segment source, which can then send them to notification tools such as Slack webhook. You can also forward these Protocols alerts to any (cloud-mode) Segment destination that accepts Track calls, including data warehouses. Most customers record these activity feed events to a data warehouse for analysis. ### How do I get notified when new violations are generated? Can I create custom violation notifications? -You can enable [violation event forwarding](/docs/protocols/validate/forward-violations/) to start delivering violations as Track calls to a Segment Source. From there, you can forward the events to any Segment destination that accepts Track calls. +You can enable [violation event forwarding](/docs/protocols/validate/forward-violations/) to start delivering violations as Track calls to a Segment source. From there, you can forward the events to any Segment destination that accepts Track calls. -You can also utilize the Slack Actions destination here. The Actions destination will allow you to set event triggers for context fields, meaning events with Violations can be sent directly from the source. +You can also use the Slack Actions destination to set event triggers for context fields, meaning events with violations are sent as Track calls directly from the source. ## Protocols Tracking Plan From cab25fb0f8c8ddcecfde2076097da95d0e6e01bf Mon Sep 17 00:00:00 2001 From: stayseesong <83784848+stayseesong@users.noreply.github.com> Date: Tue, 17 Jan 2023 09:58:49 -0800 Subject: [PATCH 40/74] Apply suggestions from code review --- src/connections/destinations/catalog/posthog/index.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/connections/destinations/catalog/posthog/index.md b/src/connections/destinations/catalog/posthog/index.md index 8f55a3c062..746101f80e 100644 --- a/src/connections/destinations/catalog/posthog/index.md +++ b/src/connections/destinations/catalog/posthog/index.md @@ -5,7 +5,7 @@ id: 5ece242d61055a0b1bb2e103 --- [PostHog](https://posthog.com/?utm_source=segmentio&utm_medium=docs&utm_campaign=partners) is self-hosted, open-source analytics product. Get the same powerful features as other product analytics software but keep full control over your data. -This destination is maintained by PostHog. For any issues with the destination, checkout the [PostHog users slack](https://posthog.com/slack) or [contact the PostHog Support team](mailto:hey@posthog.com). +This destination is maintained by PostHog. For any issues with the destination, check out the [PostHog users slack](https://posthog.com/slack) or [contact the PostHog Support team](mailto:hey@posthog.com). ## Getting Started @@ -17,9 +17,9 @@ This destination is maintained by PostHog. For any issues with the destination, 4. Go to your [PostHog set up page](https://app.posthog.com/setup), and find and copy the **API key**. 5. Enter the PostHog API Key that you copied in the PostHog destination settings in Segment. 6. Enter your PostHog instance URL, as the address to your instance **without any trailing slash**, for example: - - `https://app.posthog.com` if you are using PostHog Cloud US - - `https://eu.posthog.com` if you are using PostHog Cloud EU - - `https://posthog-example.herokuapp.com` if you are self-hosting on Heruku + - `https://app.posthog.com` if you use PostHog Cloud US + - `https://eu.posthog.com` if you use PostHog Cloud EU + - `https://posthog-example.herokuapp.com` if you self-host on Heroku ## Page From ef6cfd04f7fc6b17dfd06ff361910eb1b0b1dc4f Mon Sep 17 00:00:00 2001 From: stayseesong <83784848+stayseesong@users.noreply.github.com> Date: Tue, 17 Jan 2023 10:02:47 -0800 Subject: [PATCH 41/74] Apply suggestions from code review --- src/connections/destinations/catalog/posthog/index.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/connections/destinations/catalog/posthog/index.md b/src/connections/destinations/catalog/posthog/index.md index 746101f80e..f6ac3906fb 100644 --- a/src/connections/destinations/catalog/posthog/index.md +++ b/src/connections/destinations/catalog/posthog/index.md @@ -3,9 +3,9 @@ title: PostHog Destination rewrite: true id: 5ece242d61055a0b1bb2e103 --- -[PostHog](https://posthog.com/?utm_source=segmentio&utm_medium=docs&utm_campaign=partners) is self-hosted, open-source analytics product. Get the same powerful features as other product analytics software but keep full control over your data. +[PostHog](https://posthog.com/?utm_source=segmentio&utm_medium=docs&utm_campaign=partners){:target="_blank"} is self-hosted, open-source analytics product. Get the same powerful features as other product analytics software but keep full control over your data. -This destination is maintained by PostHog. For any issues with the destination, check out the [PostHog users slack](https://posthog.com/slack) or [contact the PostHog Support team](mailto:hey@posthog.com). +This destination is maintained by PostHog. For any issues with the destination, check out the [PostHog users slack](https://posthog.com/slack){:target="_blank"} or [contact the PostHog Support team](mailto:hey@posthog.com). ## Getting Started @@ -14,7 +14,7 @@ This destination is maintained by PostHog. For any issues with the destination, 1. From the Destinations catalog page in the Segment App, click **Add Destination**. 2. Search for "PostHog" in the Destinations Catalog, and select the PostHog destination. 3. Choose which Source should send data to the PostHog destination. -4. Go to your [PostHog set up page](https://app.posthog.com/setup), and find and copy the **API key**. +4. Go to your [PostHog set up page](https://app.posthog.com/setup){:target="_blank"}, and find and copy the **API key**. 5. Enter the PostHog API Key that you copied in the PostHog destination settings in Segment. 6. Enter your PostHog instance URL, as the address to your instance **without any trailing slash**, for example: - `https://app.posthog.com` if you use PostHog Cloud US From 282a73bd005bde452172c39ed51ccb728bf42f18 Mon Sep 17 00:00:00 2001 From: stayseesong <83784848+stayseesong@users.noreply.github.com> Date: Tue, 17 Jan 2023 10:08:32 -0800 Subject: [PATCH 42/74] Apply suggestions from code review --- src/connections/destinations/catalog/posthog/index.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/connections/destinations/catalog/posthog/index.md b/src/connections/destinations/catalog/posthog/index.md index f6ac3906fb..042f5e1428 100644 --- a/src/connections/destinations/catalog/posthog/index.md +++ b/src/connections/destinations/catalog/posthog/index.md @@ -3,7 +3,7 @@ title: PostHog Destination rewrite: true id: 5ece242d61055a0b1bb2e103 --- -[PostHog](https://posthog.com/?utm_source=segmentio&utm_medium=docs&utm_campaign=partners){:target="_blank"} is self-hosted, open-source analytics product. Get the same powerful features as other product analytics software but keep full control over your data. +[PostHog](https://posthog.com/?utm_source=segmentio&utm_medium=docs&utm_campaign=partners){:target="_blank"} is a self-hosted, open-source analytics product. Get the same powerful features as other product analytics software but keep full control over your data. This destination is maintained by PostHog. For any issues with the destination, check out the [PostHog users slack](https://posthog.com/slack){:target="_blank"} or [contact the PostHog Support team](mailto:hey@posthog.com). @@ -14,9 +14,9 @@ This destination is maintained by PostHog. For any issues with the destination, 1. From the Destinations catalog page in the Segment App, click **Add Destination**. 2. Search for "PostHog" in the Destinations Catalog, and select the PostHog destination. 3. Choose which Source should send data to the PostHog destination. -4. Go to your [PostHog set up page](https://app.posthog.com/setup){:target="_blank"}, and find and copy the **API key**. +4. Go to your [PostHog set up page](https://app.posthog.com/setup){:target="_blank"}, and copy the **API key**. 5. Enter the PostHog API Key that you copied in the PostHog destination settings in Segment. -6. Enter your PostHog instance URL, as the address to your instance **without any trailing slash**, for example: +6. Enter your PostHog instance URL as the address to your instance **without any trailing slash**, for example: - `https://app.posthog.com` if you use PostHog Cloud US - `https://eu.posthog.com` if you use PostHog Cloud EU - `https://posthog-example.herokuapp.com` if you self-host on Heroku From fdca3f9f47ab47c870eea8361ed4d71d10973db9 Mon Sep 17 00:00:00 2001 From: markzegarelli Date: Tue, 17 Jan 2023 10:46:46 -0800 Subject: [PATCH 43/74] catalog update (#4064) --- src/_data/catalog/destination_categories.yml | 2 +- src/_data/catalog/destinations.yml | 276 +++++++++--------- src/_data/catalog/destinations_private.yml | 2 +- src/_data/catalog/regional-supported.yml | 18 +- src/_data/catalog/source_categories.yml | 2 +- src/_data/catalog/sources.yml | 36 ++- .../cloud-apps/thomas-gilbert-test/index.md | 4 + .../thomas-source-test-portal/index.md | 4 + 8 files changed, 201 insertions(+), 143 deletions(-) create mode 100644 src/connections/sources/catalog/cloud-apps/thomas-gilbert-test/index.md create mode 100644 src/connections/sources/catalog/cloud-apps/thomas-source-test-portal/index.md diff --git a/src/_data/catalog/destination_categories.yml b/src/_data/catalog/destination_categories.yml index 9a656d5f65..2faf2089dc 100644 --- a/src/_data/catalog/destination_categories.yml +++ b/src/_data/catalog/destination_categories.yml @@ -1,5 +1,5 @@ # AUTOGENERATED FROM PUBLIC API. DO NOT EDIT -# destination categories last updated 2023-01-12 +# destination categories last updated 2023-01-17 items: - display_name: A/B Testing slug: a-b-testing diff --git a/src/_data/catalog/destinations.yml b/src/_data/catalog/destinations.yml index e6d359408c..7caf017bfd 100644 --- a/src/_data/catalog/destinations.yml +++ b/src/_data/catalog/destinations.yml @@ -1,5 +1,5 @@ # AUTOGENERATED FROM PUBLIC API. DO NOT EDIT -# destination data last updated 2023-01-12 +# destination data last updated 2023-01-17 items: - id: 637e8d185e2dec264895ea89 display_name: 1Flow @@ -574,10 +574,10 @@ items: type: number defaultValue: 0 description: >- - *You must enable setDelay first!* + *You must enable setDelay first!* - Set the initial delay time in seconds with the setting `setDelay` + Set the initial delay time in seconds with the setting `setDelay` enabled. The maximum delay start time of the adjust SDK is 10 seconds. required: false label: delayTime @@ -804,7 +804,7 @@ items: If you have a nested object, separate the name with a `.` For example if - you wanted to map the page referrer, you would put: page.referrer. + you wanted to map the page referrer, you would put: page.referrer. **NOTE**: By default Segment send alls your `properties` as Context Data @@ -917,7 +917,7 @@ items: description: |- Configure merchandising event, such as purchase or currency events. - This is currently in Beta Testing and not generally available. + This is currently in Beta Testing and not generally available. required: true label: 'Merchandising Events ' - name: pageNameFallbackToScreen @@ -965,7 +965,7 @@ items: description: >- Note: This setting is for Server-Side only, and only applies when the Drop Visitor ID setting is disabled and you send a marketingCloudId in the - Adobe Analytics integration object. + Adobe Analytics integration object. ​​ @@ -1312,7 +1312,7 @@ items: description: >- By default, notifications are sent to the Adobe Target backend for incrementing impression count. If false, notifications are not sent for - incrementing impression count. + incrementing impression count. placeholder: '' defaultValue: true required: false @@ -2669,14 +2669,15 @@ items: Mobile Only. If a user has granted your app location permissions, enable this setting so that the SDK will also grab the location of the user. Amplitude will never prompt the user for location permission, so this must - be done by your app. + be done by your app. required: false label: Enable Location Listening - name: endpoint type: select defaultValue: '' description: >- - Cloud-mode. Choose the endpoint corresponding to your region. EU endpoints aren't supported for device mode. + Cloud-mode Only (will not work in device-mode). Choose the endpoint + corresponding to your region. required: true label: Endpoint - name: eventUploadPeriodMillis @@ -2858,7 +2859,7 @@ items: defaultValue: true description: >- Client Side Only - Enabling this will send referrer information as a user - property to Amplitude when you call Segment's `page` method. + property to Amplitude when you call Segment's `page` method. required: false label: Track Referrer to Amplitude - name: trackRevenuePerProduct @@ -2878,7 +2879,7 @@ items: description: >- (Optional) This enables the sending of start and end session events for mobile products. Amplitude's libraries track sessions automatically and - this option is not necessary for session tracking. + this option is not necessary for session tracking. required: false label: Track Session Events to Amplitude - name: trackUtmProperties @@ -2898,7 +2899,7 @@ items: defaultValue: [] description: >- Server-Side and Mobile Only. Configure values to be appended to the user - property array via identify.traits. + property array via identify.traits. required: false label: Traits to Append - name: traitsToIncrement @@ -2915,7 +2916,7 @@ items: defaultValue: [] description: >- Server-Side and Mobile Only. Configure values to be prepended to the user - property array via identify.traits. + property array via identify.traits. required: false label: Traits to Prepend - name: traitsToSetOnce @@ -2923,7 +2924,7 @@ items: defaultValue: [] description: >- Server-Side and Mobile Only. Configure values to be set only once via - identify.traits. + identify.traits. required: false label: Traits to Set Once - name: unsetParamsReferrerOnNewSession @@ -2956,7 +2957,8 @@ items: to not fully respect the "Prefer Anonymous ID for Device ID" setting (Amplitude may set the device ID upon initialization before it gets set to the proper Anonymous ID) if using Analytics.js 1.0. Consider [updating to - Analytics.js 2.0](https://segment.com/docs/connections/sources/catalog/libraries/website/javascript/upgrade-to-ajs2/). + Analytics.js 2.0] + (https://segment.com/docs/connections/sources/catalog/libraries/website/javascript/upgrade-to-ajs2/) required: false label: Use Amplitude Referral - name: useCustomAmplitudeProperties @@ -6730,7 +6732,7 @@ items: when sent to AppsFlyer API. To prevent these event failures in this scenario enable this send the IDFV instead. When the "Can Omit AppsFlyerId" setting is enabled if the IDFA is zeroed out, we will also - send an IDFV when this setting is enabled. + send an IDFV when this setting is enabled. required: false label: >- Fallback to send IDFV when advertisingId key not present (Server-Side @@ -8064,7 +8066,7 @@ items: defaultValue: '' description: >- Select where you want Braze to receive, process, and store data from this - destination. + destination. Choose your Appboy Gateway (ie. US 01, US 02, EU 01, etc.). required: true @@ -9348,7 +9350,7 @@ items: at a different path on that server, provide a value for this option that is the absolute path to the file, e.g. /mycustompath/my-worker.js. VERY IMPORTANT: setting a value here limits the scope of push notifications on - your site. For instance, in the above example, because the service + your site. For instance, in the above example, because the service ,worker file is located within the /mycustompath/ directory, appboy.registerAppboyPushMessages MAY ONLY BE CALLED from web pages that start with http://yoursite.com/mycustompath/. @@ -10729,7 +10731,7 @@ items: description: >+ [Chartbeat expects](http://support.chartbeat.com/docs/#titles) the `document.title` ( Segment `page`'s `props.title`) to populate as - *title*. + *title*.
@@ -10739,7 +10741,7 @@ items: This setting respects Segment's legacy behavior of setting the page name and category as *title* for existing users, but defaults new users to the correct behavior of sending `document.title` as *title* to Chartbeat, and - allows current users to opt-in to the correct behavior if they chose. + allows current users to opt-in to the correct behavior if they chose. required: true label: sendNameAndCategoryAsTitle @@ -11861,7 +11863,7 @@ items: defaultValue: '' description: >- (Web/A.js Only) Add your Comscore Keyword value. This will be added to the - query string as `comscorekw={value}`. + query string as `comscorekw={value}`. required: false label: Comscore Keyword - name: consentFlag @@ -12419,7 +12421,7 @@ items: type: DATETIME description: >- Event timestamp. Optional. Date format is ISO 8601 standard. If empty, - the request upload time will be used. + the request upload time will be used. placeholder: '' defaultValue: '@path': $.timestamp @@ -13107,7 +13109,7 @@ items: Input your event name (case sensitive) on the left and choose the event type from the dropdown. If you do not define these mappings we will fall back on the default mappings defined in our - [documentation](https://segment.com/docs/connections/destinations/catalog/criteo/#track). + [documentation](https://segment.com/docs/connections/destinations/catalog/criteo/#track). required: false label: Event Mappings - name: homeUrl @@ -13827,7 +13829,7 @@ items: Destination. It should be 20 or 64 characters long, and look something like this: 91837a6c9e8b49d0ef71. An API Key is required if you're using our server-side or mobile libraries. For more details you can view the - [Customer.io Docs](https://customer.io/docs/en/segment-destination). + [Customer.io Docs](https://customer.io/docs/en/segment-destination). required: true label: API Key - name: convertToUnixTime @@ -15191,7 +15193,7 @@ items: defaultValue: false description: >- Enable setting this if you want to use the Transaction Counting Method - instead of Items Sold method when assigning `qty` value in the iframe. + instead of Items Sold method when assigning `qty` value in the iframe. required: true label: Use Transaction Counting (Client Side Only) actions: [] @@ -15465,7 +15467,7 @@ items: description: >- Please input the Segment event names on the left and their corresponding Eloqua Custom Object names on the right. This mapping is required for all - events you would like in Eloqua as Custom Objects. + events you would like in Eloqua as Custom Objects. **Note:** If you have set up Custom Object Fields in Eloqua, please ensure @@ -15482,13 +15484,13 @@ items: description: >- Please input the Segment trait names on the left and their corresponding Eloqua Custom Account Field names on the right. The traits must be set up - in the Eloqua dashboard prior to instantiating this mapping. + in the Eloqua dashboard prior to instantiating this mapping. **Note:** If you have set up Custom Account Fields in Eloqua, please ensure the corresponding Segment payload property values are of the same data type specified in Eloqua's dashboard. Eloqua will reject any event - containing a Custom Account Field with an incorrect data type. + containing a Custom Account Field with an incorrect data type. required: false label: Map Custom Traits to Accounts - name: mappedIdentifyTraits @@ -15497,13 +15499,13 @@ items: description: >- Please input the Segment trait names on the left and their corresponding Eloqua Custom Contact Field names on the right. The traits must be set up - in the Eloqua dashboard prior to instantiating this mapping. + in the Eloqua dashboard prior to instantiating this mapping. **Note:** If you have set up Custom Contact Fields in Eloqua, please ensure the corresponding Segment payload property values are of the same data type specified in Eloqua's dashboard. Eloqua will reject any event - containing a Custom Contact Field with an incorrect data type. + containing a Custom Contact Field with an incorrect data type. required: false label: Map Custom Traits to Contacts - name: password @@ -16431,7 +16433,7 @@ items: events will fail when sent to Facebook App Events API. To prevent these event failures in this scenario enable this setting to default to set the IDFA to be zero'd out (ie. '00000000-0000-0000-0000-000000000000') when - sent to Facebook App Events. + sent to Facebook App Events. required: false label: >- Fallback to Zeroed IDFA when advertisingId key not present (Server-Side @@ -18505,7 +18507,7 @@ items: description: >- For pre-purchase events such as `Product Viewed`, `Product Added`, and `Product List Viewed`, choose which Segment property you would like to map - to Facebook's `value` property. + to Facebook's `value` property. required: false label: Value Field Identifier actions: [] @@ -18684,7 +18686,7 @@ items: description: >- For pre-purchase events such as `Product Viewed` and `Product Added`, choose which Segment property you would like to map to Facebook's value - property. + property. required: true label: Value Field Identifier - name: whitelistPiiProperties @@ -22771,7 +22773,7 @@ items: events will fail when sent to Google Adwords. To prevent these event failures in this scenario enable this setting to set the IDFA to be zero'd out (ie. '00000000-0000-0000-0000-000000000000') when sent to Google - Adwords. + Adwords. required: false label: >- Fallback to Zeroed IDFA when advertisingId key not present (Server-Side @@ -22834,7 +22836,7 @@ items: defaultValue: '' description: >- Enter your GOOGLE-CONVERSION-ID. You can get this value from your global - site tag snippet. It should look something like `AW-901243031` + site tag snippet. It should look something like `AW-901243031` required: true label: Google Conversion ID - name: clickConversions @@ -23615,7 +23617,7 @@ items: Ads account. On the right-hand side, map the Segment field that contains the corresponding value See [Google’s documentation on how to create custom conversion - variables.](https://developers.google.com/google-ads/api/docs/conversions/conversion-custom-variables) + variables.](https://developers.google.com/google-ads/api/docs/conversions/conversion-custom-variables) placeholder: '' required: false multiple: false @@ -24293,7 +24295,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -24514,7 +24516,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -24685,7 +24687,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -24872,7 +24874,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -25113,7 +25115,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -25284,7 +25286,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -25483,7 +25485,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -25654,7 +25656,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -25825,7 +25827,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -25949,7 +25951,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -26194,7 +26196,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -26403,7 +26405,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -26576,7 +26578,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -26696,7 +26698,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -26841,7 +26843,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -27012,7 +27014,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -27134,7 +27136,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -27299,7 +27301,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -27437,7 +27439,7 @@ items: user-scoped dimensions to ensure custom properties are picked up by Google. See Google’s [Custom user properties](https://support.google.com/analytics/answer/9269570) to - learn how to set and register user properties. + learn how to set and register user properties. placeholder: '' required: false multiple: false @@ -27812,15 +27814,15 @@ items: type: OBJECT description: |2- - The fields to write to the spreadsheet. - - On the left-hand side, input the name of the field as it will appear in the Google Sheet. + The fields to write to the spreadsheet. + On the left-hand side, input the name of the field as it will appear in the Google Sheet. + On the right-hand side, select the field from your data model that maps to the given field in your sheet. - + --- - - + + placeholder: '' required: true multiple: false @@ -28084,7 +28086,7 @@ items: dimensions and metrics. Unlike the client side integration which has the luxury of browsers and the global window `ga` tracker, for server side we will check your `traits` and your settings for custom dimension/metric - mappings and send it with an explicit event. + mappings and send it with an explicit event. required: false label: Enable Server Side Identify - name: enhancedEcommerce @@ -28183,7 +28185,7 @@ items: additional Google Analytics trackers and want to ensure that your Segment tracker has a distinct name. If this is enabled you must prepend this tracker name to any native Google Analytics (except for create) that you - call, e.g. 'segmentGATracker.require(....)' + call, e.g. 'segmentGATracker.require(....)' required: false label: Name Tracker - name: nonInteraction @@ -32595,7 +32597,7 @@ items: description: >- The field from your Segment events that corresponds to your Kable customer's Client ID (by default, Kable uses Segment's userId). For nested - JSON fields, use dot notation. + JSON fields, use dot notation. required: true label: Client ID Field - name: kableClientId @@ -32964,7 +32966,7 @@ items: description: >- Enable this to use the Keen's data enrichment feature for parsing URLs into its components for easier filtering. Note that `userAgent` is only - collected on Android, not on iOS. + collected on Android, not on iOS. required: false label: URL Parsing Addon - name: writeKey @@ -33255,7 +33257,7 @@ items: being created inside of Klaviyo. When enabled, we will never set $id field to your `userId` when you call `.identify()` or `.track()`. Instead, we will only set $email as the primary identifier with your `traits.email` or - `properties.email`. + `properties.email`. required: false label: Enforce Email as Primary Identifier - name: listId @@ -33283,7 +33285,7 @@ items: available when sending server side events. Note that this option may result in superfluous user profiles in Klaviyo and is generally not recommend. If this option is unchecked, we will only accept server side - events that includes the `userId`. + events that includes the `userId`. required: false label: Fallback on Anonymous ID - name: useSegmentSpec @@ -38227,7 +38229,7 @@ items: defaultValue: false description: >- We used to add $ to mixpanel traits as super properties. Enable this if - you would like to use the legacy behavior. + you would like to use the legacy behavior. required: false label: Legacy Super Properties - name: people @@ -39621,7 +39623,7 @@ items: description: >- Segment will map our `asset_id` property to Nielsen's `assetId` field. If you would like to set up a custom property mapping for ad asset ids please - indicate the name of the property here. + indicate the name of the property here. required: false label: Custom Ad Asset Id Property Name - name: appId @@ -39648,7 +39650,7 @@ items: description: >- Segment will map our `asset_id` property to Nielsen's `assetId` field. If you would like to set up a custom property mapping for content asset ids - please indicate the name of the property here. + please indicate the name of the property here. required: false label: Custom Content Asset Id Property Name - name: contentLengthPropertyName @@ -39657,7 +39659,7 @@ items: description: >- Segment will map our `total_length` property to Nielsen's `length` field by default. If you would like to set up a custom property mapping please - indicate the name of the property here. + indicate the name of the property here. required: false label: Content Length Property Name - name: customSectionProperty @@ -39666,7 +39668,7 @@ items: description: >- Segment will map the page/screen `name` field to Nielsen's `section` field. If you would like to set up a custom property mapping for the - page/screen section name please indicate the name of the property here. + page/screen section name please indicate the name of the property here. required: false label: Custom Page/Screen Section Property Name - name: instanceName @@ -39694,7 +39696,7 @@ items: For livestream video positions please enable this setting if you want Segment to default to sending the current time in seconds. If you would like Segment to calculate an offset position value based of - `properties.position` do not enable this setting. + `properties.position` do not enable this setting. required: false label: Enable Default to Current Time for Livestream Playhead Position - name: sfCode @@ -40175,7 +40177,7 @@ items: Viewed" track event will be triggered each time you access an Optimizely live variable. If you're regularly accessing live variables and want to reduce the number of track events triggered, pass the "false" flag, for - example our Android library would be: + example our Android library would be: ``` @@ -40183,7 +40185,7 @@ items: Boolean myVariable = optimizelyClient.getVariableBoolean("myVariable", userId, false); - ``` + ``` And for our iOS library: @@ -40316,7 +40318,7 @@ items: Map here the properties from your event that want to pass to Optimizely as part of the Campaign. The keys for the map correspond to Segment's properties, and the values the Campaign properties. For example, `source - -> campaign_name`. + -> campaign_name`. required: false label: Custom Campaign Properties - name: customExperimentProperties @@ -40326,7 +40328,7 @@ items: Map here the properties from your event that want to pass to Optimizely as part of the Experiment. The keys for the map correspond to Segment's properties, and the values the Experiment properties. For example, `color - -> experiment_color`. + -> experiment_color`. required: false label: Custom Experiment Properties - name: listen @@ -40584,7 +40586,7 @@ items: description: >- You can find your Account ID (`piAId`) under **Marketing > Campaigns** in your [Pardot account](https://pi.pardot.com/campaign). After selecting - your desired website campaign, press **View Tracking Code**. + your desired website campaign, press **View Tracking Code**. required: true label: Account ID - name: businessUnitID @@ -40626,7 +40628,7 @@ items: label: Email Address type: STRING description: |- - The prospect's email address. + The prospect's email address. Used to upsert a prospect in Pardot. If multiple prospects have the given email, the prospect with the latest activity is updated. If there's no prospect with the given email, a prospect is created. Please note that Pardot treats email address as case sensitive and will create multiple prospects for casing differences. placeholder: '' defaultValue: @@ -40874,7 +40876,7 @@ items: description: >- If true, the request’s search includes deleted records. This property only affects [AMPSEA - accounts](https://help.salesforce.com/s/articleView?id=sf.pardot_admin_ampsea_parent.htm&type=5). + accounts](https://help.salesforce.com/s/articleView?id=sf.pardot_admin_ampsea_parent.htm&type=5). If all records with a matching email address are deleted, the one with the latest activity is undeleted and updated. Otherwise, a new prospect is created. placeholder: '' defaultValue: true @@ -40890,7 +40892,7 @@ items: type: OBJECT description: |2- - Additional prospect fields to send to Pardot. + Additional prospect fields to send to Pardot. Only editable fields are accepted. Please see [Pardot docs](https://developer.salesforce.com/docs/marketing/pardot/guide/prospect-v5.html#fields) for more details. On the left-hand side, input the Pardot field name. On the right-hand side, map the Segment field that contains the value. placeholder: '' required: false @@ -43092,7 +43094,7 @@ items: Please enter the trait you would like to be mapped to `EMAIL_PEERMISSION_STATUS_`. This will allow you to track users who opt in and out of marketing communications in your apps and websites. The value - of this trait MUST be a boolean. + of this trait MUST be a boolean. required: false label: Email Permission Status Trait Mapping - name: optOutMobileTrait @@ -43102,7 +43104,7 @@ items: Please enter the trait youw would like to be mapped to MOBILE_PERMISSION_STATUS_. This will allow you to track users who opt in and out of marketing communications in your apps and websites. The value - of this trait MUST be a boolean. + of this trait MUST be a boolean. required: false label: Mobile Permission Status Trait Mapping - name: password @@ -44330,7 +44332,7 @@ items: description: >- Salescamp only track events that mention in below list. Salescamp recommend you to add event in below list that important for your sales - process (ex. "Demo Request Placed", "Order Placed" etc.) + process (ex. "Demo Request Placed", "Order Placed" etc.) required: true label: 'Events ' actions: [] @@ -44615,13 +44617,13 @@ items: The fields used to find Salesforce records for updates. **This is required if the operation is Update or Upsert.** - Any field can function as a matcher, including Record ID, External IDs, standard fields and custom fields. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. - + Any field can function as a matcher, including Record ID, External IDs, standard fields and custom fields. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. + If multiple records are found, no updates will be made. **Please use fields that result in unique records.** - + --- - + placeholder: '' required: false multiple: false @@ -44677,10 +44679,10 @@ items: Additional fields to send to Salesforce. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. This can include standard or custom fields. Custom fields must be predefined in your Salesforce account and the API field name should have __c appended. - + --- - - + + placeholder: '' required: true multiple: false @@ -44763,13 +44765,13 @@ items: The fields used to find Salesforce records for updates. **This is required if the operation is Update or Upsert.** - Any field can function as a matcher, including Record ID, External IDs, standard fields and custom fields. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. - + Any field can function as a matcher, including Record ID, External IDs, standard fields and custom fields. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. + If multiple records are found, no updates will be made. **Please use fields that result in unique records.** - + --- - + placeholder: '' required: false multiple: false @@ -44987,10 +44989,10 @@ items: Additional fields to send to Salesforce. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. This can include standard or custom fields. Custom fields must be predefined in your Salesforce account and the API field name should have __c appended. - + --- - - + + placeholder: '' required: false multiple: false @@ -45073,13 +45075,13 @@ items: The fields used to find Salesforce records for updates. **This is required if the operation is Update or Upsert.** - Any field can function as a matcher, including Record ID, External IDs, standard fields and custom fields. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. - + Any field can function as a matcher, including Record ID, External IDs, standard fields and custom fields. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. + If multiple records are found, no updates will be made. **Please use fields that result in unique records.** - + --- - + placeholder: '' required: false multiple: false @@ -45186,10 +45188,10 @@ items: Additional fields to send to Salesforce. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. This can include standard or custom fields. Custom fields must be predefined in your Salesforce account and the API field name should have __c appended. - + --- - - + + placeholder: '' required: false multiple: false @@ -45272,13 +45274,13 @@ items: The fields used to find Salesforce records for updates. **This is required if the operation is Update or Upsert.** - Any field can function as a matcher, including Record ID, External IDs, standard fields and custom fields. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. - + Any field can function as a matcher, including Record ID, External IDs, standard fields and custom fields. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. + If multiple records are found, no updates will be made. **Please use fields that result in unique records.** - + --- - + placeholder: '' required: false multiple: false @@ -45589,10 +45591,10 @@ items: Additional fields to send to Salesforce. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. This can include standard or custom fields. Custom fields must be predefined in your Salesforce account and the API field name should have __c appended. - + --- - - + + placeholder: '' required: false multiple: false @@ -45675,13 +45677,13 @@ items: The fields used to find Salesforce records for updates. **This is required if the operation is Update or Upsert.** - Any field can function as a matcher, including Record ID, External IDs, standard fields and custom fields. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. - + Any field can function as a matcher, including Record ID, External IDs, standard fields and custom fields. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. + If multiple records are found, no updates will be made. **Please use fields that result in unique records.** - + --- - + placeholder: '' required: false multiple: false @@ -45902,10 +45904,10 @@ items: Additional fields to send to Salesforce. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. This can include standard or custom fields. Custom fields must be predefined in your Salesforce account and the API field name should have __c appended. - + --- - - + + placeholder: '' required: false multiple: false @@ -45988,13 +45990,13 @@ items: The fields used to find Salesforce records for updates. **This is required if the operation is Update or Upsert.** - Any field can function as a matcher, including Record ID, External IDs, standard fields and custom fields. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. - + Any field can function as a matcher, including Record ID, External IDs, standard fields and custom fields. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. + If multiple records are found, no updates will be made. **Please use fields that result in unique records.** - + --- - + placeholder: '' required: false multiple: false @@ -46047,10 +46049,10 @@ items: Additional fields to send to Salesforce. On the left-hand side, input the Salesforce field API name. On the right-hand side, map the Segment field that contains the value. This can include standard or custom fields. Custom fields must be predefined in your Salesforce account and the API field name should have __c appended. - + --- - - + + placeholder: '' required: false multiple: false @@ -46312,7 +46314,7 @@ items: type: STRING description: >- The unique key for an event definition in Salesforce Marketing Cloud. - The event defintion must be predefined in SFMC. + The event defintion must be predefined in SFMC. placeholder: '' required: true multiple: false @@ -46910,7 +46912,7 @@ items: type: string defaultValue: '' description: >- - Enter URL for the AWS S3 bucket name or Azure Blob Storage container. + Enter URL for the AWS S3 bucket name or Azure Blob Storage container. (e.g. 'https://my-storage.blob.core.windows.net/my-container' ). required: true label: BUCKET CONTAINER URL @@ -48864,7 +48866,7 @@ items: Reference: [Get All field definitions](https://docs.sendgrid.com/api-reference/custom-fields/get-all-field-definitions) --- - + placeholder: '' required: false multiple: false @@ -48984,7 +48986,7 @@ items: your code versus other code. By default, Sentry displays all frames as expanded. Once you add values to this setting, all frames excepts for those you've added here will appear as collapsed in Sentry. You can still - manually expand all frames from a stack trace in your Sentry dashboard. + manually expand all frames from a stack trace in your Sentry dashboard. It would be suggested to add the current page url, and the host for your CDN here. required: false @@ -49594,7 +49596,7 @@ items: defaultValue: '' description: >- Use "handlebarsjs" templating to format messages sent to Slack. The - default template is "Identified {{name}}. + default template is "Identified {{name}}. {{traits}}". You do not need to append ‘traits’, as this template is specific to `.identify()` calls only and thus is assumed. Make sure @@ -55222,7 +55224,7 @@ items: description: >- If you have any product properties that you would like to map to item-scoped custom attributes inside `site_event_items` for your ecommerce - events, please specify them here. + events, please specify them here. required: false label: Custom Item Attributes actions: [] @@ -55396,7 +55398,7 @@ items: Twitter supports having a single universal website tag for conversions. Please enter your Universal Website Tag Pixel ID. This setting is also required in order to use [Advanced Conversion - Tracking](https://business.twitter.com/en/help/campaign-measurement-and-analytics/conversion-tracking-for-websites.html). + Tracking](https://business.twitter.com/en/help/campaign-measurement-and-analytics/conversion-tracking-for-websites.html). required: false label: Universal Website Tag Pixel ID actions: [] @@ -56629,7 +56631,7 @@ items: defaultValue: '' description: >- WalkMe environment that should be loaded (e.g. Test, Production), you can - find it in the Editor's Snippet area + find it in the Editor's Snippet area required: true label: Environment - name: integrityHash @@ -56655,7 +56657,7 @@ items: defaultValue: '' description: >- WalkMe system ID - you can find it in the Editor's Snippet area (GUID - parameter) + parameter) required: true label: WalkMe system ID actions: [] @@ -57968,7 +57970,7 @@ items: no `userId` is present, the integration will only create or update an organization on `.group()`. To do so, your Zendesk authorization must be configured as an "Agent". **NOTE:** If you would like to link users to - organizations on group calls, do not enable this setting. + organizations on group calls, do not enable this setting. required: false label: Send Group Calls Without UserId - name: events @@ -57990,7 +57992,7 @@ items: description: >- Enable this setting if you would like to remove users organization memberships on `.identify()` calls when you pass a `company.id` trait and - the `company.remove` trait is set to true. + the `company.remove` trait is set to true. required: false label: Enable Removing Users from Organizations - name: subdomain diff --git a/src/_data/catalog/destinations_private.yml b/src/_data/catalog/destinations_private.yml index d434f91366..7758ed4b1d 100644 --- a/src/_data/catalog/destinations_private.yml +++ b/src/_data/catalog/destinations_private.yml @@ -1,5 +1,5 @@ # AUTOGENERATED FROM PUBLIC API. DO NOT EDIT -# destination data last updated 2023-01-12 +# destination data last updated 2023-01-17 items: - id: 62b256147cbb49302d1486d0 display_name: Heap Web (Actions) diff --git a/src/_data/catalog/regional-supported.yml b/src/_data/catalog/regional-supported.yml index 80e28609ac..9baebdae0b 100644 --- a/src/_data/catalog/regional-supported.yml +++ b/src/_data/catalog/regional-supported.yml @@ -1,5 +1,5 @@ # AUTOGENERATED LIST OF CONNECTIONS THAT SUPPORT REGIONAL -# Last updated 2023-01-12 +# Last updated 2023-01-17 warehouses: - id: WcjBCzUGff display_name: Azure SQL Data Warehouse @@ -743,6 +743,22 @@ sources: - us endpoints: - us + - id: vOlNDkP5qR + display_name: Thomas Gilbert Test + slug: thomas-gilbert-test + url: connections/sources/catalog/cloud-apps/thomas-gilbert-test + regions: + - us + endpoints: + - us + - id: 6nGX8vnUpM + display_name: Thomas Source Test Portal + slug: thomas-source-test-portal + url: connections/sources/catalog/cloud-apps/thomas-source-test-portal + regions: + - us + endpoints: + - us - id: StjIoJAuBT display_name: Thomas Test Source slug: thomas-test-source diff --git a/src/_data/catalog/source_categories.yml b/src/_data/catalog/source_categories.yml index 281eed6f2c..bfe787acda 100644 --- a/src/_data/catalog/source_categories.yml +++ b/src/_data/catalog/source_categories.yml @@ -1,5 +1,5 @@ # AUTOGENERATED FROM PUBLIC API. DO NOT EDIT -# source cateogries last updated 2023-01-12 +# source cateogries last updated 2023-01-17 items: - display_name: A/B Testing slug: a-b-testing diff --git a/src/_data/catalog/sources.yml b/src/_data/catalog/sources.yml index 766e379821..a5e34052da 100644 --- a/src/_data/catalog/sources.yml +++ b/src/_data/catalog/sources.yml @@ -1,5 +1,5 @@ # AUTOGENERATED FROM PUBLIC API. DO NOT EDIT -# sources last updated 2023-01-12 +# sources last updated 2023-01-17 items: - id: 8HWbgPTt3k display_name: .NET @@ -1102,7 +1102,7 @@ items: isCloudEventSource: true slug: paytronix url: connections/sources/catalog/cloud-apps/paytronix - hidden: false + hidden: true regions: - us endpoints: @@ -1537,6 +1537,38 @@ items: url: https://cdn.filepicker.io/api/file/s6u7GXDeRBq7chdP2Vet categories: - A/B Testing + - id: vOlNDkP5qR + display_name: Thomas Gilbert Test + isCloudEventSource: true + slug: thomas-gilbert-test + url: connections/sources/catalog/cloud-apps/thomas-gilbert-test + hidden: false + regions: + - us + endpoints: + - us + source_type: cloud-app + description: '' + logo: + url: '' + categories: [] + - id: 6nGX8vnUpM + display_name: Thomas Source Test Portal + isCloudEventSource: true + slug: thomas-source-test-portal + url: connections/sources/catalog/cloud-apps/thomas-source-test-portal + hidden: false + regions: + - us + endpoints: + - us + source_type: cloud-app + description: This is a description + logo: + url: https://cdn.filepicker.io/api/file/1SsbNbflStyRV9E0euoi + categories: + - Advertising + - Customer Success - id: StjIoJAuBT display_name: Thomas Test Source isCloudEventSource: false diff --git a/src/connections/sources/catalog/cloud-apps/thomas-gilbert-test/index.md b/src/connections/sources/catalog/cloud-apps/thomas-gilbert-test/index.md new file mode 100644 index 0000000000..f6f7440aa1 --- /dev/null +++ b/src/connections/sources/catalog/cloud-apps/thomas-gilbert-test/index.md @@ -0,0 +1,4 @@ +--- +title: 'Thomas Gilbert Test Source' +hidden: true +--- \ No newline at end of file diff --git a/src/connections/sources/catalog/cloud-apps/thomas-source-test-portal/index.md b/src/connections/sources/catalog/cloud-apps/thomas-source-test-portal/index.md new file mode 100644 index 0000000000..4482c785ae --- /dev/null +++ b/src/connections/sources/catalog/cloud-apps/thomas-source-test-portal/index.md @@ -0,0 +1,4 @@ +--- +title: 'Thomas Source Test Portal Source' +hidden: true +--- \ No newline at end of file From 937705c11636431521f31aeebf37a467b310177e Mon Sep 17 00:00:00 2001 From: Jazma Foskin <82051355+jfoskin@users.noreply.github.com> Date: Tue, 17 Jan 2023 15:50:07 -0500 Subject: [PATCH 44/74] Added Proxy Sources Note cccccbbujrtrjbcnildcbuhchngfgrccvnuugiufffcl --- .../catalog/libraries/website/javascript/custom-proxy.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/connections/sources/catalog/libraries/website/javascript/custom-proxy.md b/src/connections/sources/catalog/libraries/website/javascript/custom-proxy.md index bb907461cd..951251c19e 100644 --- a/src/connections/sources/catalog/libraries/website/javascript/custom-proxy.md +++ b/src/connections/sources/catalog/libraries/website/javascript/custom-proxy.md @@ -22,6 +22,9 @@ You need to set up two important parts, regardless of the CDN provider you use: - Proxy to Segment CDN (`cdn.segment.com`) - Proxy to Segment tracking API (`api.segment.io`) +> info "" +> Please be advised that Segment only has the ability to enable the proxy setting for our web AJS source. Mobile source proxy details will be within the source documentation. At this time it is not possible to setup a proxy for server sources with Segment's UI. + ## Set up Follow the directions listed for CloudFront or use your own CDN setup. Once you complete those steps and verify that your proxy works for both `cdn.segment.com` and `api.segment.io`, [contact Segment Product Support](https://segment.com/help/contact/) with the following template email: From cc7f079004331a9486a091fefe450f590b7a3a91 Mon Sep 17 00:00:00 2001 From: markzegarelli Date: Wed, 18 Jan 2023 08:07:10 -0800 Subject: [PATCH 45/74] Add new product limits for Engage (#3965) --- src/engage/product-limits.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/engage/product-limits.md b/src/engage/product-limits.md index 1068915350..5ec06e4922 100644 --- a/src/engage/product-limits.md +++ b/src/engage/product-limits.md @@ -21,14 +21,17 @@ To learn more about custom limits and upgrades, contact your dedicated Customer ## Audiences and Computed Traits -| name | limit | Details | -| --------------------------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| Compute Concurrency | 5 concurrent audiences | Segment computes five new audiences or computed traits at a time. Once the limit is reached, Segment queues additional audience computations until one of the five audiences finishes computing. | -| Compute Throughput | 10000 computations per second | Computations include any Track or Identify call that triggers an audience or computed trait re-computation. Once the limit is reached, Segment may slow audience processing. | -| Events Lookback History | 3 years | The period of time for which Segment stores audience and computed traits computation events. | -| Real-time to batch destination sync frequency | 2-3 hours | The frequency with which Segment syncs real-time audiences to batch destinations. | -| Event History | `1970-01-01` | Events with a timestamp less than `1970-01-01` aren't always ingested, which could impact audience backfills with event timestamps prior to this date. | -| Event Properties (Computed Traits) | 10,000 | For Computed Traits that exceed this limit, Segment will not persist any new Event Properties and will drop new trait keys and corresponding values. | +| name | limit | Details | +| --------------------------------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Compute Concurrency | 5 concurrent audiences | Segment computes five new audiences or computed traits at a time. Once the limit is reached, Segment queues additional audience computations until one of the five audiences finishes computing. | +| Compute Throughput | 10000 computations per second | Computations include any Track or Identify call that triggers an audience or computed trait re-computation. Once the limit is reached, Segment may slow audience processing. | +| Events Lookback History | 3 years | The period of time for which Segment stores audience and computed traits computation events. | +| Real-time to batch destination sync frequency | 2-3 hours | The frequency with which Segment syncs real-time audiences to batch destinations. | +| Event History | `1970-01-01` | Events with a timestamp less than `1970-01-01` aren't always ingested, which could impact audience backfills with event timestamps prior to this date. | +| Engage Data Ingest | 1x the data ingested into Connections | The amount of data transferred into the Compute Engine. | +| Audience Frequency Update | 1 per 8 hours | Audiences that require time windows, [funnels](/docs/engage/audiences/#funnel-audiences), [dynamic properties](/docs/engage/audiences/#dynamic-property-references), or [account-level membership](/docs/engage/audiences/#account-level-audiences) are processed on chronological schedules. Unless otherwise agreed upon, the audiences will compute at the limit set forth. | +| Event Properties (Computed Traits) | 10,000 | For Computed Traits that exceed this limit, Segment will not persist any new Event Properties and will drop new trait keys and corresponding values. | + ## SQL Traits From 4bb8ecf5603b1242e603dbbd2f114b1068fbcbb4 Mon Sep 17 00:00:00 2001 From: Sai <115584728+saitwilio@users.noreply.github.com> Date: Wed, 18 Jan 2023 10:18:39 -0800 Subject: [PATCH 46/74] Updating to exclude authenticating via email/token because it doesn't work (#4045) * Updating to exclude authenticating via email/token * Update src/connections/destinations/catalog/zendesk/index.md Co-authored-by: markzegarelli * Clarify setup instructions Co-authored-by: Sai Gattu Co-authored-by: markzegarelli --- .../destinations/catalog/zendesk/index.md | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/connections/destinations/catalog/zendesk/index.md b/src/connections/destinations/catalog/zendesk/index.md index f890103cbd..4323da903c 100644 --- a/src/connections/destinations/catalog/zendesk/index.md +++ b/src/connections/destinations/catalog/zendesk/index.md @@ -13,10 +13,7 @@ id: 54521fdc25e721e32a72ef06 1. From the Segment web app, click **Catalog**. 2. Search for "Zendesk" in the Catalog, select it, and choose which of your sources to connect the destination to. -3. There are two ways to authenticate your Zendesk account with Segment: - * Use the standard email and password you use to Sign In to your Zendesk account. In the Zendesk settings, add your email in the **Email** setting and your password in the **Password** setting. - * Use Zendesk OAuth with a unique token. Get the corresponding token from your Zendesk account: **Settings > Channels > API** and under the Settings Tab choose the corresponding token from the "Active API Tokens" list. In the Zendesk settings, add your `email/token` in the **Email** setting (for example, `peter@intech.com/token` - use the actual word token in your email address) and add the actual token in the **Password** setting. -4. Add your Zendesk subdomain in the **Subdomain** setting (not including `.zendesk.com`). +3. Enter your Zendesk domain (not including `.zendesk.com`) and click **Connect**. The Zendesk OAuth login opens in a new tab. Sign in with your Zendesk credentials to authenticate and allow the Segment integration. ## Identify @@ -171,17 +168,6 @@ When you call `group` Segment inserts or update an organization in Zendesk and u Here's an example: -{% comment %} api-example '{ - "action": "group", - "groupId": "908172409", - "userId": "6789", - "traits": { - "name": "LA Lakers", - "url": "https://lakers.com", - "deleted": false - } -}'}}} {% endcomment %} - ```js { "action": "group", From de7e4d33691f25dddf1f590251bd3ca53fe8ca0e Mon Sep 17 00:00:00 2001 From: Sarah Rudy <78389005+sarahrudy@users.noreply.github.com> Date: Wed, 18 Jan 2023 11:20:27 -0700 Subject: [PATCH 47/74] Add links to Braze Swift and Kotlin plugin repositories (#4058) * Update index.md * Update src/connections/destinations/catalog/braze/index.md Co-authored-by: markzegarelli --- src/connections/destinations/catalog/braze/index.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/connections/destinations/catalog/braze/index.md b/src/connections/destinations/catalog/braze/index.md index 0e5f0c56ed..4ac1dc4ed6 100644 --- a/src/connections/destinations/catalog/braze/index.md +++ b/src/connections/destinations/catalog/braze/index.md @@ -17,10 +17,12 @@ The Braze Destination is open-sourced on GitHub. Source code for the following i - [iOS](https://github.com/Appboy/appboy-segment-ios) (maintained by Braze) - [Android](https://github.com/Appboy/appboy-segment-android)(maintained by Braze) +- [Swift](https://github.com/braze-inc/analytics-swift-braze)(maintained by Braze) +- [Kotlin](https://github.com/braze-inc/braze-segment-kotlin)(maintained by Braze) - [Web](https://github.com/segment-integrations/analytics.js-integration-appboy) (maintained by Segment) - [Server](https://github.com/segmentio/integration-appboy) (maintained by Segment) -For issues with iOS or Android platforms, contact Braze support. For issues with Web or Server platforms, contact [Segment support](https://segment.com/help/contact). +For issues with mobile platforms (iOS, Android, Swift, or Kotlin), contact Braze support. For issues with Web or Server platforms, contact [Segment support](https://segment.com/help/contact). > info "The Braze SDK contains three major versions" > If you are migrating from version 1 to version 2, see information about [migration from Version 1 to Version 2](/docs/connections/destinations/catalog/braze/#migrating-to-v2-of-the-braze-web-sdk) below. From fba1b07568d67e35e5511e22fbabae09299ca42d Mon Sep 17 00:00:00 2001 From: bobbyatsegment <93934274+bobbyatsegment@users.noreply.github.com> Date: Wed, 18 Jan 2023 12:53:15 -0700 Subject: [PATCH 48/74] Update Public API docs to highlight eligibility for PAPI access (#4036) * Update index.md * Add plan grid to Public API page Co-authored-by: markzegarelli --- src/_data/products.yml | 9 +++++++++ src/api/public-api/index.md | 7 +++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/_data/products.yml b/src/_data/products.yml index a35287ac8b..cb72aa7871 100644 --- a/src/_data/products.yml +++ b/src/_data/products.yml @@ -145,3 +145,12 @@ items: team: false business: true add-on: false + +- product_display_name: Public API + slug: papi + plan-note: "The Public API is available to customers on Team or Business plans." + plans: + free: false + team: true + business: true + add-on: false \ No newline at end of file diff --git a/src/api/public-api/index.md b/src/api/public-api/index.md index 34ed7b1d07..68f256927a 100644 --- a/src/api/public-api/index.md +++ b/src/api/public-api/index.md @@ -1,7 +1,8 @@ --- title: Public API +plan: papi --- -The Segment Public API helps you manage your Segment workspaces and its resources. You can use the API to perform CRUD (create, read, update, delete) operations at no extra charge. This includes working with resources such as Sources, Destinations, Warehouses, Tracking Plans, and the Segment Destinations and Sources Catalogs. +The Segment Public API helps you manage your Segment workspaces and its resources. You can use the API to perform CRUD (create, read, update, delete) operations at no extra charge. This includes working with resources such as Sources, Destinations, Warehouses, Tracking Plans, and the Segment Destinations and Sources Catalogs. The Public API is available to Team and Business Tier customers. All CRUD endpoints in the API follow REST conventions and use standard HTTP methods. Different URL endpoints represent different resources in a workspace. @@ -26,6 +27,7 @@ The Public API includes the following benefits over the Config API: | Available in Europe | The Public API is accessible to both US and EU-based workspaces. | | Increased reliability | The Public API features more stable endpoints, and a 99.8% success rate | + ## API Token Security To enhance API token security, Segment partners with GitHub to prevent fraudulent use of exposed API tokens found in public git repositories. This helps to prevent malicious actors from using exposed tokens to perform unauthorized actions in your Segment workspace. @@ -45,4 +47,5 @@ Developers can accidentally commit tokens to public repositories, exposing them By automatically revoking the exposed token, Segment helps keep your workspace secure and prevents potential abuse of the token. #### How do I enable this feature? -This feature is automatically enabled for all workspaces on Team or Business tier plans. \ No newline at end of file +This feature is automatically enabled for all workspaces on Team or Business tier plans. + From 6f8a77a4f6e6bc7f555d7fc181cd92dd48fe6dcc Mon Sep 17 00:00:00 2001 From: forstisabella <92472883+forstisabella@users.noreply.github.com> Date: Wed, 18 Jan 2023 16:04:54 -0500 Subject: [PATCH 49/74] Apply suggestions from code review --- .../catalog/libraries/website/javascript/custom-proxy.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/connections/sources/catalog/libraries/website/javascript/custom-proxy.md b/src/connections/sources/catalog/libraries/website/javascript/custom-proxy.md index 951251c19e..33341ec76c 100644 --- a/src/connections/sources/catalog/libraries/website/javascript/custom-proxy.md +++ b/src/connections/sources/catalog/libraries/website/javascript/custom-proxy.md @@ -22,8 +22,8 @@ You need to set up two important parts, regardless of the CDN provider you use: - Proxy to Segment CDN (`cdn.segment.com`) - Proxy to Segment tracking API (`api.segment.io`) -> info "" -> Please be advised that Segment only has the ability to enable the proxy setting for our web AJS source. Mobile source proxy details will be within the source documentation. At this time it is not possible to setup a proxy for server sources with Segment's UI. +> info " " +> Segment only has the ability to enable the proxy setting for the Web (Analytics.js) source. Details for mobile source proxies are in the [Analytics for iOS](/docs/connections/sources/catalog/libraries/mobile/ios/#proxy-https-calls) and [Analytics for Android](/docs/connections/sources/catalog/libraries/mobile/android/#proxying-http-calls) documentation. It is not currently possible to set up a proxy for server sources using the Segment UI. ## Set up From 9c516a6d89ea1808a67bece8321513e51d2bcf1a Mon Sep 17 00:00:00 2001 From: Sarah Rudy <78389005+sarahrudy@users.noreply.github.com> Date: Wed, 18 Jan 2023 16:00:24 -0700 Subject: [PATCH 50/74] Add a note that some Actions-based destinations do not support IE11 (#4066) * Update supported-browsers.md Actions destinations not yet supported for IE11 * Update src/connections/sources/catalog/libraries/website/javascript/supported-browsers.md Co-authored-by: markzegarelli --- .../catalog/libraries/website/javascript/supported-browsers.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/connections/sources/catalog/libraries/website/javascript/supported-browsers.md b/src/connections/sources/catalog/libraries/website/javascript/supported-browsers.md index 013eb8d15e..ddff1a20f2 100644 --- a/src/connections/sources/catalog/libraries/website/javascript/supported-browsers.md +++ b/src/connections/sources/catalog/libraries/website/javascript/supported-browsers.md @@ -15,10 +15,13 @@ The library is regularly tested and is functional with the following browsers: - Microsoft Edge - Brave + ### Internet Explorer Support Segment guarantees support for Internet Explorer 11 and later for Analytics.js. Remember that different bundled (device-mode) destinations might have different compatibility guarantees for their own products. Refer to the vendor's documentation to confirm browser compatibility. +> info "" +> Classic destinations and Analytics.js support Internet Explorer 11, but some Actions destinations are not yet supported. ## Tracking Protection (ITP, ETP) From 6ba0d662ec62faee8cb67dbd5ca5d12e82e20f05 Mon Sep 17 00:00:00 2001 From: Sarah Rudy <78389005+sarahrudy@users.noreply.github.com> Date: Wed, 18 Jan 2023 16:02:52 -0700 Subject: [PATCH 51/74] Update index.md (#4047) Add Segment's GitHub repo to page --- src/connections/destinations/catalog/matomo/index.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/connections/destinations/catalog/matomo/index.md b/src/connections/destinations/catalog/matomo/index.md index d24e905d26..aa3379b220 100644 --- a/src/connections/destinations/catalog/matomo/index.md +++ b/src/connections/destinations/catalog/matomo/index.md @@ -4,7 +4,10 @@ rewrite: true redirect_from: '/connections/destinations/catalog/piwik/' id: 54521fda25e721e32a72eee7 --- -[Matomo](https://matomo.org/), formerly Piwik, is the leading open source web analytics platform that gives you valuable insights into your website's visitors, your marketing campaigns and much more, so you can optimize your strategy and online experience of your visitors. +[Matomo](https://matomo.org/), formerly Piwik, is the leading open source web analytics platform that gives you valuable insights into your website's visitors, your marketing campaigns and much more, so you can optimize your strategy and online experience of your visitors. + +Segment’s Matomo destination code is open-source and can be viewed on GitHub: +- [Javascript](https://github.com/segmentio/analytics.js-integrations/blob/master/integrations/piwik/lib/index.js){:target="_blank"} ## Getting Started From 7e26349f3396c51b861a7fa1ba92a28cc7a6c2ac Mon Sep 17 00:00:00 2001 From: stayseesong <83784848+stayseesong@users.noreply.github.com> Date: Wed, 18 Jan 2023 17:15:57 -0800 Subject: [PATCH 52/74] Apply suggestions from code review Co-authored-by: markzegarelli --- .../sources/catalog/libraries/server/node/classic.md | 4 ++-- .../sources/catalog/libraries/server/node/migration.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/connections/sources/catalog/libraries/server/node/classic.md b/src/connections/sources/catalog/libraries/server/node/classic.md index d8bb4159e1..f2df7647eb 100644 --- a/src/connections/sources/catalog/libraries/server/node/classic.md +++ b/src/connections/sources/catalog/libraries/server/node/classic.md @@ -92,7 +92,7 @@ Field | Details `anonymousId` _String, optional_ | An ID associated with the user when you don't know who they are (for example, [the anonymousId generated by `analytics.js`](/docs/connections/sources/catalog/libraries/website/javascript/#anonymous-id)). _Note: You must include at least one of `userId` or `anonymousId` in all identify calls._ `traits` _Object, optional_ | A dictionary of [traits](/docs/connections/spec/identify#traits) you know about the user. Things like: `email`, `name` or `friends`. `timestamp` _Date, optional_ | A JavaScript date object representing when the identify call took place. If the identify just happened, leave it out as Segment will use the server's time. If you're importing data from the past make sure you to send a `timestamp`. -`context` _Object, optional_ | A dictionary of extra [context](https://segment.com/docs/connections/spec/common/#context) to attach to the call. _Note: `context` differs from `traits` because it is not attributes of the user itself._ +`context` _Object, optional_ | A dictionary of extra [context](/docs/connections/spec/common/#context) to attach to the call. _Note: `context` differs from `traits` because it is not attributes of the user itself._ Find details on the **identify method payload** in the Segment [Spec](/docs/connections/spec/identify/). @@ -143,7 +143,7 @@ Field | Details `event` _String_ | The name of the event you're tracking. Segment recommends you use human-readable names like `Song Played` or `Status Updated`. `properties` _Object, optional_ | A dictionary of properties for the event. If the event was `Product Added`, it might have properties like `price` or `product`. `timestamp` _Date, optional_ | A JavaScript date object representing when the track took place. If the track just happened, leave it out as Segment will use the server's time. If you're importing data from the past make sure you to send a `timestamp`. -`context` _Object, optional_ | A dictionary of extra [context](https://segment.com/docs/connections/spec/common/#context) to attach to the call. _Note: `context` differs from `traits` because it is not attributes of the user itself._ +`context` _Object, optional_ | A dictionary of extra [context](/docs/connections/spec/common/#context) to attach to the call. _Note: `context` differs from `traits` because it is not attributes of the user itself._ Find details on **best practices in event naming** as well as the **`track` method payload** in the Segment [Spec](/docs/connections/spec/track/). diff --git a/src/connections/sources/catalog/libraries/server/node/migration.md b/src/connections/sources/catalog/libraries/server/node/migration.md index b80245bd44..0e501be368 100644 --- a/src/connections/sources/catalog/libraries/server/node/migration.md +++ b/src/connections/sources/catalog/libraries/server/node/migration.md @@ -31,7 +31,7 @@ If you're using the [classic version of Analytics Node.js](/docs/connections/sou ```javascript const analytics = new Analytics({ writeKey: '' }) ``` -3. Change flushing to [graceful shutdown](/docs/connections/sources/catalog/libraries/server/node//#graceful-shutdown). +3. Change flushing to [graceful shutdown](/docs/connections/sources/catalog/libraries/server/node/#graceful-shutdown).
Before: ```javascript From 36639782dc2971b1a3f02c02554fb4cc9f699774 Mon Sep 17 00:00:00 2001 From: pwseg Date: Wed, 18 Jan 2023 21:20:46 -0600 Subject: [PATCH 53/74] Add Amplitude Session ID Info #3972 --- .../catalog/actions-amplitude/index.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/connections/destinations/catalog/actions-amplitude/index.md b/src/connections/destinations/catalog/actions-amplitude/index.md index 4d594ab5d7..2fec77b27c 100644 --- a/src/connections/destinations/catalog/actions-amplitude/index.md +++ b/src/connections/destinations/catalog/actions-amplitude/index.md @@ -41,13 +41,13 @@ Initially, the Log Event Action was reporting purchases to Amplitude for all eve To resolve this, purchase reporting takes place in a new Action called Log Purchase. -For instances created prior to before the Log Purchases action was released, you need to manually add the Log Purchases Action to report purchases to Amplitude. +For instances created prior to before the Log Purchases action was released, you need to manually add the Log Purchases Action to report purchases to Amplitude. -To manually add the Log Purchases Action: +To manually add the Log Purchases Action: 1. Add a new Mapping for the Log Purchases Action. The default trigger for this action is Order Completed events. 2. Modify the Trigger if you need to report purchases for any other events. -3. Modify the Trigger of Log Event to exclude these same events. This helps you to avoid sending the same event twice. -4. Enable the Log Purchases mapping. +3. Modify the Trigger of Log Event to exclude these same events. This helps you to avoid sending the same event twice. +4. Enable the Log Purchases mapping. ### Connection Modes for Amplitude (Actions) destination @@ -65,8 +65,9 @@ By default, Segment maps the Segment property `context.device.id` to the Amplitu ### Enable session tracking for Analytics.js 2.0 -The session tracking is automatically enabled on JavaScript sources. +Session tracking is automatically enabled on JavaScript sources. +The session ID Segment passes to Amplitude is stored locally in a key-value pair. You can access the session ID by viewing the value associated with the `analytics_session_id` key. ### Enable Amplitude session tracking for Swift @@ -93,7 +94,7 @@ To enable session tracking in Amplitude when using the [Segment Kotlin library]( To enable session tracking in Amplitude when using the [Segment iOS library](https://github.com/segmentio/analytics-ios): 1. Add the [Amplitude Session middleware](https://github.com/segment-integrations/analytics-ios-integration-amplitude/blob/amplitude-session/Pod/Classes/SEGAmplitudeSession.m) to your project. -2. Add the middleware & enable `trackApplicationLifecycleEvents` in your configuration +2. Add the middleware & enable `trackApplicationLifecycleEvents` in your configuration: ```objective-c NSString *const SEGMENT_WRITE_KEY = @" ... "; SEGAnalyticsConfiguration *configuration = [SEGAnalyticsConfiguration configurationWithWriteKey:SEGMENT_WRITE_KEY]; @@ -109,7 +110,7 @@ To enable session tracking in Amplitude when using the [Segment Android library] ```gradle implementation 'com.segment.analytics.android.integrations:amplitude:3.1.0' ``` -3. Add the middleware & enable `trackApplicationLifecycleEvents` in your configuration +3. Add the middleware & enable `trackApplicationLifecycleEvents` in your configuration: ```java String SEGMENT_WRITE_KEY = " ... "; analytics = new Analytics.Builder(this, SEGMENT_WRITE_KEY) From 64848f601a69a19dfa5c3d1f241dcfc6419dd7be Mon Sep 17 00:00:00 2001 From: pwseg Date: Wed, 18 Jan 2023 21:49:00 -0600 Subject: [PATCH 54/74] Fix Youbora Destination Typos --- .../destinations/catalog/youbora/index.md | 146 +++++------------- 1 file changed, 38 insertions(+), 108 deletions(-) diff --git a/src/connections/destinations/catalog/youbora/index.md b/src/connections/destinations/catalog/youbora/index.md index 3c2bb7de4d..09498e2ecc 100644 --- a/src/connections/destinations/catalog/youbora/index.md +++ b/src/connections/destinations/catalog/youbora/index.md @@ -11,89 +11,39 @@ Youbora automatically starts recording data. ## Tracking Video Events Segment can keep track of events occurring on any number of video players on your -page. **You must include the `session_id` propert with every video event you want to send to the Youbora so Segment can keep track of which player to attribute the events to.** +page. **You must include the `session_id` property with every video event you want to send to Youbora so Segment can keep track of which player to attribute the events to.** ### Video Playback Started -When a user starts playback of a video, use the [Video Playback Started](/docs/connections/spec/video/#playback-events) event. Segment maps the properties -from the Video Playback Started event to the following Youbora video metadata -fields: - - - - - - - - - - - - - - - - - -
**Youbora Parameter****Segment Property****Data Type**
'content.isLive'`properties.livestream`Boolean
Resource`context.page.url`String
+When a user starts playback of a video, use the [Video Playback Started](/docs/connections/spec/video/#playback-events) event. Segment maps the properties from the Video Playback Started event to the following Youbora video metadata fields: + +| Youbora Parameter | Segment Property | Data Type | +| ----------------- | ----------------------- | --------- | +| `content.isLive` | `properties.livestream` | Boolean | +| Resource | `context.page.url` | String | + ### Video Content Started -When the video content actually begins playing, use the [Video Content -Started](/docs/connections/spec/video/#content-events) event. Segment maps the properties -from the Video Playback Started event to the following Youbora video metadata -fields: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
**Youbora Parameter****Segment Property****Data Type**
'content.title'`properties.title`String
'content.title2'`properties.program`String
'content.duration'`properties.total_length`Integer
'content.metadata.content_id'`properties.asset_id`String
'content.metadata.genre'`properties.genre`String
'content.metadata.owner'`properties.publisher`String
- -Using the difference in time between when `Video Playback Started` and `Video -Content Started`, Youbora will calculate the join time for you. +When the video content actually begins playing, use the [Video Content Started](/docs/connections/spec/video/#content-events) event. Segment maps the properties from the Video Playback Started event to the following Youbora video metadata fields: + +| Youbora Parameter | Segment Property | Data Type | +|:----------------------------- |:------------------------- |:--------- | +| `content.title` | `properties.title` | String | +| `content.title2` | `properties.program` | String | +| `content.duration` | `properties.total_length` | Integer | +| `content.metadata.content_id` | `properties.asset_id` | String | +| `content.metadata.genre` | `properties.genre` | String | +| `content.metadata.owner` | `properties.publisher` | String | + +Youbora calculates the join time using the time difference between `Video Playback Started` and `Video Content Started`, ### Video Playback Paused/Resumed -When a user pauses/resumes playback of a video, use the [Video -Playback Paused](/docs/connections/spec/video/#playback-events) and [Video Playback -Resumed](/docs/connections/spec/video/#playback-events) events. +When a user pauses or resumes playback of a video, use the [Video Playback Paused](/docs/connections/spec/video/#playback-events) and [Video Playback Resumed](/docs/connections/spec/video/#playback-events) events. -If the user pauses during an ad, be sure to fill the -`properties.ad_asset_id` field from our spec for **both** calls, as we use its +If the user pauses during an ad, be sure to fill the `properties.ad_asset_id` field from the spec for **both** calls, as Segment uses its presence to determine whether the pause is occurring during an ad or not. **Example** @@ -112,11 +62,7 @@ analytics.track('Video Playback Resumed', { ### Video Playback Seek Started/Completed -When the video content actually begins playing, use the [Video -Playback Seek Started](/docs/connections/spec/video/#playback-events) and [Video Playback -Seek Completed](/docs/connections/spec/video/#playback-events) events. Youbora internally -calculates the duration of the seek but if you would prefer to provide this -value yourself you can pass it as the integration-specific option `duration`. +When the video content actually begins playing, use the [Video Playback Seek Started](/docs/connections/spec/video/#playback-events) and [Video Playback Seek Completed](/docs/connections/spec/video/#playback-events) events. Youbora internally calculates the duration of the seek but if you would prefer to provide this value yourself you can pass it as the integration-specific option `duration`. **Example** @@ -133,10 +79,6 @@ Playback Buffer Started](/docs/connections/spec/video/#playback-events) and [Vid Buffer Completed](/docs/connections/spec/video/#playback-events) events. Segment maps the properties from these events to the following Youbora video metadata fields: -If the buffer occurs during an ad, be sure to fill the -`properties.ad_asset_id` field from our spec for **both** calls, as we use its -presence to determine whether the buffer is occurring during an ad or not. - **Example** ```js @@ -145,14 +87,16 @@ analytics.track('Video Playback Buffer Started', { session_id: 1 }); analytics.track('Video Playback Buffer Completed', { session_id: 1 }); ``` +If the user pauses during an ad, be sure to fill the `properties.ad_asset_id` field from the spec for **both** calls, as Segment uses its +presence to determine whether the pause is occurring during an ad or not. + ### Video Playback Interrupted When playback of a video is interrupted, use the [Video Playback Interrupted](/docs/connections/spec/video/#playback-events) event. ### Video Playback Completed -To track the completion of the video playback session, use our [Video -Playback Completed](/docs/connections/spec/video/#playback-events) event. +To track the completion of the video playback session, use the [Video Playback Completed](/docs/connections/spec/video/#playback-events) event. **Example** @@ -165,18 +109,10 @@ analytics.track('Video Playback Completed', { session_id: 1 }); When an ad begins to load, use the [Video Ad Started](/docs/connections/spec/video/#ad-events) event. Segment maps the properties from these events to the following Youbora video metadata fields: - - - - - - - - - - - -
**Youbora Parameter****Segment Property****Data Type**
'ad.title'`properties.title`String
+| Youbora Parameter | Segment Property | Data Type | +| ----------------- | ------------------ | --------- | +| `ad.title` | `properties.title` | String | + **Example** @@ -186,8 +122,7 @@ analytics.track('Video Ad Started', { session_id: 1, title: 'Test Ad Title', ad_ ### Video Ad Completed -To track the completion of an ad, use our [Video Ad -Completed](/docs/connections/spec/video/#ad-events) event. +To track the completion of an ad, use the [Video Ad Completed](/docs/connections/spec/video/#ad-events) event. **Example** @@ -214,13 +149,9 @@ Youbora supports automatic video tracking for the following video players: - ThePlatform - VideoJS -However, note that relying solely on Youbora auto tracking will not send your -video events to Segment downstream destinations, including a raw data warehouse. -To track data to downstream tools, we recommend either manually implementing all -video player events or manually implementing all events alongside Youbora. If -you employ the latter method, you should indicate explicitly that your Segment -events should not flow to Youbora (because they've already been auto-tracked by -the Youbora library). +However, relying solely on Youbora auto tracking will not send your video events to Segment downstream destinations, including a raw data warehouse. To track data to downstream tools, Segment recommends either manually implementing all video player events or manually implementing all events alongside Youbora. + +If you employ the latter method, you should indicate explicitly that your Segment events should not flow to Youbora (because they've already been auto-tracked by the Youbora library). ```javascript analytics.track('Video Playback Started', { // empty properties object @@ -231,10 +162,9 @@ analytics.track('Video Playback Started', { // empty properties object }); ``` -In order to track a player that falls in one of the above categories, follow the -below steps: +Use the following steps to track a player that falls in one of the previous categories: -1. Ensure you have the latest snippet on your page (Updated 2/6/18). +1. Ensure you have the latest snippet on your page. 2. If your snippet is in the `head` of your page, move it to the very bottom of your `body`, right before the `` tag. 3. Replace the `load` method in your snippet with the following (you can delete @@ -272,7 +202,7 @@ In the `options` field, you can pass options the same way you would pass them natively to Youbora as documented [here](http://developer.nicepeopleatwork.com/plugins/general/setting-youbora-options/). -See the below example for what a working implementation looks like: +The following example shows a working implementation: ```js