├── .github └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── Makefile ├── Readme.md ├── buildpack └── boreal │ ├── index.js │ └── window.js ├── destinations.test.js ├── destinations ├── adobe-target │ ├── Readme.md │ └── handler.js ├── airtable │ ├── handler.js │ └── readme.md ├── datadog │ ├── Readme.md │ └── handler.js ├── follow-along │ ├── Readme.md │ └── handler.js ├── ipstack │ ├── Readme.md │ └── handler.js ├── lob │ └── handler.js ├── optimizely │ ├── Readme.md │ └── handler.js ├── qualtrics │ ├── handler.js │ └── required-settings.png ├── requestbin │ ├── Readme.md │ └── handler.js ├── salesloft │ └── handler.js ├── sift │ ├── README.md │ └── handler.js ├── slack │ ├── Readme.md │ └── handler.js ├── wavefront │ ├── README.md │ └── handler.js └── zendesk │ ├── handler.js │ └── readme.md ├── package.json ├── sources.test.js ├── sources ├── Yotpo │ ├── handler.js │ └── readme.md ├── adyen │ ├── handler.js │ └── webhook-examples │ │ ├── authorisation.json │ │ ├── orderOpened.json │ │ └── refundWithData.json ├── audit-forwarding │ ├── Readme.md │ ├── handler.js │ └── webhook-examples │ │ ├── audience*.json │ │ ├── computedTrait*.json │ │ ├── destinationFilter*.json │ │ ├── integration*.json │ │ ├── newEventAllowed.json │ │ ├── permissionCheck.json │ │ ├── personasWarehouse*.json │ │ ├── schema*.json │ │ ├── source*.json │ │ ├── space*.json │ │ ├── trackingPlan*.json │ │ └── warehouse*.json ├── close-io │ ├── handler.js │ └── webhook-examples │ │ ├── activity_call_created.json │ │ ├── activity_call_updated.json │ │ ├── activity_contact_updated.json │ │ ├── activity_email_updated.json │ │ ├── activity_note_created.json │ │ ├── activity_note_updated.json │ │ ├── contact_created.json │ │ ├── contact_updated.json │ │ ├── lead_created.json │ │ ├── lead_updated.json │ │ └── opportunity_created.json ├── default │ ├── handler.js │ └── webhook-examples │ │ └── payload.json ├── formstack │ ├── handler.js │ └── webhook-examples │ │ └── form.json ├── github │ ├── handler.js │ └── webhook-examples │ │ ├── issue-closed.json │ │ ├── issue-comment-created.json │ │ ├── issue-opened.json │ │ └── ping.json ├── google-sheets │ ├── handler.js │ └── webhook-examples │ │ └── row-change.json ├── influitive │ ├── .DS_Store │ ├── handler.js │ └── webhook-examples │ │ ├── advocateJoined.json │ │ ├── challengeCompleted.json │ │ ├── earnedBadge.json │ │ └── groupJoined.json ├── leanplum │ ├── handler.js │ └── webhook-examples │ │ ├── emailClicked.json │ │ └── emailSent.json ├── paypal │ ├── handler.js │ └── webhook-examples │ │ ├── billingPlanActivated.json │ │ ├── billingPlanCompleted.json │ │ ├── billingPlanCreated.json │ │ ├── checkoutOrderCompleted.json │ │ ├── paymentCaptureCompleted.json │ │ ├── paymentCaptureDenied.json │ │ └── paymentOrderCreated.json ├── pipedrive │ ├── handler.js │ └── webhook-examples │ │ ├── dealAdded.json │ │ ├── dealDeleted.json │ │ ├── dealUpdated.json │ │ ├── noteAdded.json │ │ ├── organizationAdded.json │ │ ├── organizationUpdated.json │ │ ├── personAdded.json │ │ └── personUpdated.json ├── sendgrid │ ├── handler.js │ └── readme.md ├── shippo │ ├── handler.js │ └── webhook-examples │ │ └── delivered.json ├── shopify │ ├── handler.js │ └── webhook-examples │ │ └── Order.json ├── stripe-events │ ├── handler.js │ └── webhook-examples │ │ ├── charge-succeeded.json │ │ ├── customer-created.json │ │ ├── subscription-created.json │ │ └── trial-will-end-soon.json ├── survey-monkey │ ├── handler.js │ └── webhook-examples │ │ └── responseCompleted.json ├── talkable │ ├── handler.js │ └── webhook-examples │ │ ├── claim_signup_web_hook.json │ │ ├── offer_signup_web_hook.json │ │ ├── post_share_web_hook.json │ │ ├── referral_web_hook.json │ │ └── reward_web_hook.json ├── typeform │ ├── handler.js │ └── webhook-examples │ │ └── form_response.json └── zoominfo │ ├── handler.js │ └── readme.md └── yarn.lock /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push] 3 | 4 | jobs: 5 | build: 6 | name: yarn test 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@master 10 | - uses: actions/setup-node@v1 11 | with: 12 | node-version: '8.x' 13 | - run: yarn 14 | - run: yarn test 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Segment 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | yarn test -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Segment Functions Library 2 | 3 | > [!NOTE] 4 | > Segment has paused maintenance on this project, but may return it to an active status in the future. Issues and pull requests from external contributors are not being considered, although internal contributions may appear from time to time. The project remains available under its open source license for anyone to use. 5 | 6 | This repository contains a set of community-generated functions, to serve 7 | as examples to base your own functions upon. If you're building out a new 8 | integration, or a custom piece of code that you want Segment to run, use 9 | this repo as the set of examples. 10 | 11 | ![](https://github.com/segmentio/functions-library/workflows/CI/badge.svg) 12 | 13 | ## Sources 14 | 15 | - [Adyen](./sources/adyen) - Subscribes to Adyen webhooks 16 | - [Audit Forwarding](./sources/audit-forwarding) - Enhances Segment Audit Forwarding 17 | - [Close.io](./sources/close-io) - Subscribes to Close.io webhooks 18 | - [Formstack](./sources/formstack) - Subscribes to Formstack webhooks 19 | - [Github](./sources/github) - Subscribes to Github webhooks 20 | - [Google Sheets](./sources/google-sheets) - Subscribes to Google Sheets webhooks 21 | - [Influitive](./sources/influitive) - Subscribes to Influitive webhooks 22 | - [LeanPlum](./sources/leanplum) - Subscribes to Leanplum webhooks 23 | - [Paypal](./sources/paypal) - Subscribes to Paypal webhooks 24 | - [Pipedrive](./sources/pipedrive) - Subscribes to Pipedrive webhooks 25 | - [Shippo](./sources/shippo) - Subscribes to Shippo webhooks 26 | - [Shopify](./sources/shopify) - Subscribes to Shopify webhooks 27 | - [Stripe Events](./sources/stripe-events) - Subscribes to Stripe webhooks 28 | - [SurveyMonkey](./sources/survey-monkey) - Subscribes to SurveyMonkey webhooks 29 | - [Talkable](./sources/talkable) - Subscribes to Talkable webhooks 30 | - [Typeform](./sources/typeform) - Subscribes to Typeform webhooks 31 | 32 | 33 | ## Destinations 34 | 35 | - [Airtable](./destinations/airtable) - Capture user feedback and send through to your Airtable 36 | - [Follow Along](./destinations/follow-along) - Generates Fullstory links and sends to Slack 37 | - [Requestbin](./destinations/requestbin) - Sends events to RequestBin for introspection 38 | - [Slack](./destinations/slack) - Adds a Gravatar icon to events with an email and sends messages to Slack 39 | - [Zendesk](./destinations/zendesk) - Create new Zendesk tickets triggered by events that you send 40 | - [Datadog](./destinations/datadog) - Sends a metric to datadog with high level message/event type as tags 41 | - [Optimizely](./destinations/optimizely) - Sends conversion metrix to optimizely. 42 | 43 | ## Development 44 | 45 | Run tests with: 46 | 47 | ``` 48 | yarn && yarn test 49 | ``` 50 | -------------------------------------------------------------------------------- /destinations.test.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | const nock = require('nock') 3 | const process = require('process') 4 | const { processDestinationPayload } = require('./buildpack/boreal') 5 | const { EventNotSupported, InvalidEventPayload, ValidationError } = require('./buildpack/boreal/window') 6 | 7 | const destinations = fs.readdirSync(`${__dirname}/destinations`) 8 | const skips = [] 9 | 10 | describe.each(destinations)("%s", (dest) => { 11 | let dir = `${__dirname}/destinations/${dest}` 12 | 13 | let tester = test 14 | if (skips.indexOf(dest) > -1) { 15 | tester = xtest 16 | } 17 | 18 | let events = [ 19 | ["track", { 20 | "type": "track", 21 | "event": "Registered", 22 | "userId": "test-user-23js8", 23 | "timestamp": "2019-04-08T01:19:38.931Z", 24 | "email": "test@example.com", 25 | "properties": { 26 | "plan": "Pro Annual", 27 | "accountType": "Facebook", 28 | } 29 | }], 30 | ["identify", { 31 | "type": "identify", 32 | "traits": { 33 | "name": "Peter Gibbons", 34 | "email": "peter@initech.com", 35 | "plan": "premium", 36 | "logins": 5 37 | } 38 | }], 39 | ] 40 | 41 | let settings = { 42 | "apiKey": "abcd1234", 43 | } 44 | 45 | // intercept every HTTP call and return JSON 46 | // TODO: load responses from response-examples/ JSON 47 | nock(/.*/) 48 | .get(/.*/) 49 | .reply(200, {}) 50 | .post(/.*/) 51 | .reply(200, {}) 52 | 53 | tester.each(events)("%s event", async (name, event) => { 54 | process.chdir(dir) 55 | try { 56 | await processDestinationPayload({ event, settings }) 57 | } catch (err) { 58 | if (!(err instanceof EventNotSupported || err instanceof ValidationError || err instanceof InvalidEventPayload)) { 59 | fail(err) 60 | } 61 | } 62 | }) 63 | }) -------------------------------------------------------------------------------- /destinations/adobe-target/Readme.md: -------------------------------------------------------------------------------- 1 | This integration connects Personas audiences and computed traits to Adobe Target profiles. This uses the 'Single Profile Update' API call, documented here: https://developers.adobetarget.com/api/#updating-profiles 2 | 3 | Tested using v2.2.0 of Adobe's Target SDK, also known as at.js (Dec 6 2019). 4 | 5 | To identify the correct profile and "link" updates back to a client-side experience for personalization, Adobe Target server-side updates require either a "pcId" or an "mbox3rdPartyId". This implementation makes use of the "pcId". As such, this ID must be retrieved from the Adobe Target JS SDK, and stored in Segment Personas for subsequent reference / swapping-in as the ID for the update. Please see the prerequisites, below. 6 | 7 | ==========Important Prerequisites:========== 8 | These simple steps are required to make this work: 9 | 10 | 1) Retrieve the mboxPcId from your user's client-side Adobe cookie. Here's a simple code snippet that fetches this value from the cookie and stores this as a "target-id" trait: 11 | 12 | let cookie = Object.fromEntries(document.cookie.split('; ').map(x => x.split('='))) 13 | let mboxPC = cookie.mbox.split("|")[0].split("#")[1] 14 | analytics.identify({"target-id": mboxPC}); 15 | 16 | 2) Set your config options at the top of handler.js, including specifying your Personas Space and Adobe Client Code. If you use a key other than "target-id", be sure to alter the "adobeIdTraitName" variab. 17 | -------------------------------------------------------------------------------- /destinations/adobe-target/handler.js: -------------------------------------------------------------------------------- 1 | /* README 2 | This integration connects Personas audiences and computed traits to Adobe Target profiles. This uses the 'Single Profile Update' API call, documented here: https://developers.adobetarget.com/api/#updating-profiles 3 | 4 | Tested using v2.2.0 of Adobe's Target SDK, also known as at.js (Dec 6 2019). 5 | 6 | To identify the correct profile and "link" updates back to a client-side experience for personalization, Adobe Target server-side updates require either a "pcId" or an "mbox3rdPartyId". This implementation makes use of the "pcId". As such, this ID must be retrieved from the Adobe Target JS SDK, and stored in Segment Personas for subsequent reference / swapping-in as the ID for the update. Please see the prerequisites, below. 7 | 8 | ==========Important Prerequisites:========== 9 | These simple steps are required to make this work: 10 | 1) Retrieve the mboxPcId from your user's client-side Adobe cookie. Here's a simple code snippet that fetches this value from the cookie and stores this as a "target-id" trait: 11 | 12 | let cookie = Object.fromEntries(document.cookie.split('; ').map(x => x.split('='))) 13 | let mboxPC = cookie.mbox.split("|")[0].split("#")[1] 14 | analytics.identify({"target-id": mboxPC}); 15 | 16 | 2) Set your config options below, including specifying your Personas Space and Adobe Client Code. If you use a key other than "target-id", be sure to alter the "adobeIdTraitName" variab. 17 | 18 | */ 19 | 20 | /* MANDATORY CONFIG */ 21 | 22 | let adobeClientCode = "" //Your Adobe Client Code. If you don’t know your client code, in the Target UI click Setup > Implementation > Edit At.js/Mbox.js Settings. The client code is shown in the Client field. 23 | let adobeIdTraitName = "target-id" //Per the Prerequisites above, you must collect Adobe's Mbox PCID onto your Personas user profiles in Segment, for matching purposes. Please confirm the property name you are using. 24 | 25 | let profileSpaceID = "" //Your Segment Personas Space ID, so that this function can fetch from the Profile API. Find this in Personas > Settings > API Access. 26 | let profileApiToken = "" //An Access Token, to read from your Personas Instance. Grant an API Secret via Personas > Settings > API Access. 27 | 28 | /* END CONFIG */ 29 | 30 | 31 | 32 | let targetProfileUpdateUrl = "https://"+adobeClientCode+".tt.omtrdc.net/m2/"+adobeClientCode+"/profile/update?mboxPC=IDENTITY-PLACEHOLDER" 33 | let profileUrl = "https://profiles.segment.com/v1/spaces/"+profileSpaceID+"/collections/users/profiles/user_id:UID-PLACEHOLDER/traits?include="+adobeIdTraitName 34 | let adobeIdValue = "" 35 | 36 | let profileApiAuthHeaders = new Headers({ 37 | Authorization: "Basic "+btoa(profileApiToken+":") 38 | }) 39 | 40 | async function onIdentify(event, settings) { 41 | //Check mandatory config 42 | if(!checkMandatoryConfig()) 43 | throw new ValidationError("Adobe and Profile API mandatory credentials were not set") 44 | 45 | // first, grab adobe target mbox PCID. if it doesn't exist, this user cannot be updated in adobe target. 46 | // replace userId to fetch from Personas 47 | if ('userId' in event && event['userId'] != null) { 48 | profileUrl = profileUrl.replace( 49 | 'UID-PLACEHOLDER', 50 | 'user_id:' + event.userId 51 | ); 52 | } else if ('anonymousId' in event) { 53 | profileUrl = profileUrl.replace( 54 | 'UID-PLACEHOLDER', 55 | 'anonymous_id:' + event.anonymousId 56 | ); 57 | } 58 | 59 | const fetchIdFromPersonas = await fetch(profileUrl, { 60 | method: "GET", 61 | headers: profileApiAuthHeaders 62 | }) 63 | 64 | let profileApiResponse = await fetchIdFromPersonas.json() 65 | if (adobeIdTraitName in profileApiResponse.traits) { 66 | adobeIdValue = profileApiResponse.traits[adobeIdTraitName] 67 | } 68 | 69 | //update identity on the adobe target update call 70 | targetProfileUpdateUrl = targetProfileUpdateUrl.replace("IDENTITY-PLACEHOLDER", adobeIdValue) 71 | 72 | Object.entries(event.traits).forEach(function (kvPair) { 73 | let queryStringAddition = "&profile."+kvPair[0]+"="+kvPair[1] 74 | console.log(queryStringAddition) 75 | targetProfileUpdateUrl += queryStringAddition 76 | }) 77 | 78 | const updateTargetProfile = await fetch(targetProfileUpdateUrl, { 79 | method: "GET" 80 | }) 81 | return await targetProfileUpdateUrl.text 82 | } 83 | 84 | function checkMandatoryConfig() { 85 | return !!adobeClientCode && !!adobeIdTraitName && !!profileSpaceID && !!profileApiToken; 86 | } 87 | -------------------------------------------------------------------------------- /destinations/airtable/handler.js: -------------------------------------------------------------------------------- 1 | async function onTrack(event, settings) { 2 | const endpoint = `https://api.airtable.com/v0/${settings.appId}/General%20User%20Survey` 3 | if (event.event = "Feedback Added") { 4 | 5 | let favString = event.properties.favoriteFeatures || ""; 6 | let favFeaturesArray = favString.split(','); 7 | let leastFavString = event.properties.leastFavoriteFeatures || ""; 8 | let leastFavFeaturesArray = leastFavString.split(','); 9 | 10 | let airTableEvent = { 11 | "fields": { 12 | "Additional Notes": event.properties.additionalNotes, 13 | "Name": event.properties.name, 14 | "PorchCam Experience": event.properties.porchCamExperience, 15 | "Other security tools?": event.properties.otherSecurityTools, 16 | "Recommend to another?": event.properties.recommendToAnother, 17 | "Favorite Features": favFeaturesArray, 18 | "Email": event.properties.email, 19 | "Usage (# Weeks)": event.properties.usage, 20 | "Least Favorite Features": leastFavFeaturesArray 21 | } 22 | } 23 | 24 | const init = { 25 | body: JSON.stringify(airTableEvent), 26 | headers: { 27 | "Authorization": `Bearer ${settings.apiKey}`, 28 | "Content-Type": "application/json" 29 | }, 30 | method: "post" 31 | } 32 | 33 | const res = await fetch(endpoint, init) 34 | return res.json() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /destinations/airtable/readme.md: -------------------------------------------------------------------------------- 1 | # Airtable Destination Function 2 | 3 | This function helps you add user feedback directly into Airtable's [User Feedback template](https://airtable.com/templates/product-design-and-ux/expoiiRjvXfMHtXtC/user-feedback) once you've collected this information on your website. 4 | 5 | ## Getting Started 6 | 1. Add the [User Feedback template](https://airtable.com/templates/product-design-and-ux/expoiiRjvXfMHtXtC/user-feedback) as a base to your Airtable account. 7 | 2. In the Settings Builder, make sure you add the relevant [Settings as listed below](#settings), making sure the naming matches what's in the code on lines 2 and 27. You can find the Settings Builder under App Info > Settings (left hand nav). 8 | 3. Copy and paste the Airtable function template code in the `index.js` file directly into the editor. 9 | 4. Add your relevant Airtable [API Key](https://airtable.com/account) and [App ID](https://community.airtable.com/t/what-is-the-app-id-where-do-i-find-it/2984) so you can send a test event. 10 | 5. You can send the following test event to validate the existing template works as expected and see the feedback populate in your table: 11 | ``` 12 | { 13 | "type": "track", 14 | "event": "Feedback Added", 15 | "userId": "userId888", 16 | "timestamp": "2019-04-08T01:19:38.931Z", 17 | "email": "test@example.com", 18 | "properties": { 19 | "additionalNotes": "Wish the battery life was a little big longer!", 20 | "name": "Katrina Peterson", 21 | "otherSecurityTools": "I currently also use a Nest Cam for indoor monitoring because I've had it for 2+ years", 22 | "recommendToAnother": "5. Very Likely", 23 | "favoriteFeatures": "Alerts,Live video,Recording", 24 | "email": "kpeterson@test.com", 25 | "usage": 10, 26 | "leastFavoriteFeatures": "Alerts,Live video,Recording" 27 | } 28 | } 29 | ``` 30 | 6. You're done! Feel free to modify the template to suit your organization's specific needs. 31 | 32 | 33 | ## Settings 34 | 35 | - `apiKey` (string) 36 | - `appId` (string) 37 | -------------------------------------------------------------------------------- /destinations/datadog/Readme.md: -------------------------------------------------------------------------------- 1 | # Datadog Custom Destination Function 2 | 3 | This function forwards events to datadog as a `count` metric type with tags for sourceId, message type, and event name. 4 | 5 | The config is at the top of the function, and dictates the metric name (ie. `segment.messages`) and tag names that we'll use in incrementing the count. Generally, it's best to use the same tag keys and metric names across all your sources so that you can create a global dashboards and slice across the same dimensions across sources, etc. 6 | 7 | ## Settings 8 | 9 | - `apiKey` (string) ([DataDog API key](https://segment.datadoghq.com/account/settings#api)) 10 | -------------------------------------------------------------------------------- /destinations/datadog/handler.js: -------------------------------------------------------------------------------- 1 | 2 | // CONFIG - defaults are probably best, but feel free to update 3 | // 4 | // nb. all of these "config" inputs could be updated to be "parameterized" 5 | // so that you could dynamically set them in the UI (like apiKey) 6 | // via per-source via settings values in the future 7 | 8 | // if you dont want one of these tags below, you can just comment them out 9 | // EVENT_TYPE_TAG is probably riskiest — if you have a ton of event names / dynamic events 10 | // you could go over the cardinality limits 11 | const SOURCE_ID_TAG = 'source' 12 | const MESSAGE_TYPE_TAG = 'type' 13 | const EVENT_TYPE_TAG = 'event' 14 | 15 | const METRIC_NAME = 'segment.messages' 16 | 17 | async function onTrack(msg, { apiKey }) { 18 | return sendMetric(msg, apiKey) 19 | } 20 | 21 | async function onIdentify(msg, { apiKey }) { 22 | return sendMetric(msg, apiKey) 23 | } 24 | 25 | async function onGroup(msg, { apiKey }) { 26 | return sendMetric(msg, apiKey) 27 | } 28 | 29 | async function onPage(msg, { apiKey }) { 30 | return sendMetric(msg, apiKey) 31 | } 32 | 33 | async function onAlias(msg, { apiKey }) { 34 | return sendMetric(msg, apiKey) 35 | } 36 | 37 | async function onScreen(msg, { apiKey }) { 38 | return sendMetric(msg, apiKey) 39 | } 40 | 41 | async function sendMetric(msg, apiKey) { 42 | const metric = { 43 | metric: METRIC_NAME, 44 | points: [[Math.round(Date.now() / 1000), 1]], 45 | type: 'count', 46 | tags: [] 47 | } 48 | 49 | if (EVENT_TYPE_TAG && msg.type === 'track') 50 | metric.tags.push(`${EVENT_TYPE_TAG}:${msg.event}`) 51 | if (MESSAGE_TYPE_TAG) 52 | metric.tags.push(`${MESSAGE_TYPE_TAG}:${msg.type}`) 53 | if (SOURCE_ID_TAG) 54 | metric.tags.push(`${SOURCE_ID_TAG}:${msg.projectId}`) 55 | 56 | const res = await fetch(`https://api.datadoghq.com/api/v1/series?api_key=${apiKey}`, { 57 | body: JSON.stringify({ series: [ metric ] }), 58 | headers: new Headers({ 59 | "Content-Type": "application/json", 60 | }), 61 | method: "post" 62 | }) 63 | 64 | return await res.json() 65 | } 66 | -------------------------------------------------------------------------------- /destinations/follow-along/Readme.md: -------------------------------------------------------------------------------- 1 | # Follow Along 2 | 3 | This function automatically posts to Slack whenever a user completes one of the 4 | allowed actions. The message payload will contain a link to Fullstory so 5 | that you can understand what just led the user to this point 6 | 7 | ## Settings 8 | 9 | - `apiKey` (string) the slack OAuth token to use to post messages 10 | - `events` (string) a comma-separated list of the events to allow ("Completed Order,Signed Up") 11 | - `channel` (string) the slack channel to post to ("#sf") 12 | -------------------------------------------------------------------------------- /destinations/follow-along/handler.js: -------------------------------------------------------------------------------- 1 | // track demonstrates how to POST data to an external API using fetch 2 | // https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch 3 | async function onTrack(event, settings) { 4 | let evt = event.event 5 | if(!isEventAllowed(evt, settings)) return 6 | 7 | let userId = event.userId 8 | if(!userId) return 9 | 10 | let text = `A user just completed ${evt}: <${fullstoryURL(userId)}|fullstory>` 11 | const endpoint = "https://slack.com/api/chat.postMessage" 12 | const res = await fetch(endpoint, { 13 | body: JSON.stringify({ 14 | text: text, 15 | token: settings.apiKey, 16 | channel: settings.channel, 17 | }), 18 | headers: { 19 | Authorization: `Bearer ${settings.apiKey}`, 20 | "Content-Type": "application/json", 21 | }, 22 | method: "post", 23 | }) 24 | 25 | return res.text() 26 | } 27 | 28 | // returns whether an event is allowed in the settings 29 | function isEventAllowed(event, settings) { 30 | if(!settings.events) return false 31 | let arr = settings.events.split(",") 32 | if(arr.indexOf(event) > -1) return true 33 | return false 34 | } 35 | 36 | // generate the fullstory url from the `userId` 37 | function fullstoryURL(userId) { 38 | return `https://app.fullstory.com/ui/1JO/segments/everyone/people:search:((NOW%2FDAY-29DAY:NOW%2FDAY%2B1DAY):((UserAppKey:==:%22${userId}%22)):():():():)/0` 39 | } 40 | -------------------------------------------------------------------------------- /destinations/ipstack/Readme.md: -------------------------------------------------------------------------------- 1 | # IPStack Custom Destination Function 2 | 3 | This function forwards events to IPStack and writes back enriched data to another Segment source. 4 | 5 | ## Settings 6 | 7 | - `apiKey` (string) ([IPStack API key](https://ipstack.com/)) 8 | - `sourceWriteKey` (string) The Segment source write key of the source that will receive geo data 9 | -------------------------------------------------------------------------------- /destinations/ipstack/handler.js: -------------------------------------------------------------------------------- 1 | const endpoint = "http://api.ipstack.com/" 2 | 3 | // enriches an identify call and writes it back to a separate source in Segment 4 | async function onIdentify(event, settings) { 5 | if (!settings.sourceWriteKey) { 6 | throw new ValidationError("Source write key is required") 7 | } 8 | 9 | // the write key of the Segment source you want to send geo data to 10 | const writeKey = btoa(settings.sourceWriteKey) 11 | 12 | if (event.context && event.context.ip && event.userId) { 13 | const ip = event.context.ip 14 | const userId = event.userId 15 | 16 | const resp = await fetch(endpoint + ip + '?access_key=' + settings.apiKey ) 17 | var loc = await resp.json(); 18 | 19 | // format the identify call via HTTP req 20 | const ep = "https://api.segment.io/v1/identify" 21 | var identify = {userId : userId, traits: loc} 22 | 23 | const res = await fetch(ep, { 24 | body: JSON.stringify(identify), 25 | headers: new Headers({ 26 | Authorization: "Basic " + writeKey, 27 | "Content-Type": "application/json", 28 | }), 29 | method: "post" 30 | }) 31 | return res.json() 32 | } 33 | else { 34 | throw new EventNotSupported(`${event.type} missing IP or userId`) 35 | } 36 | } -------------------------------------------------------------------------------- /destinations/lob/handler.js: -------------------------------------------------------------------------------- 1 | const endpoint = "https://api.lob.com/v1/postcards" 2 | 3 | async function onTrack(event, settings) { 4 | // Only call the Lob API if the item failed to be sent to the customer 5 | if (event.event == "Item failed") { 6 | return await callLobApi(event.properties.name, event.properties.address, settings); 7 | } else { 8 | return; 9 | } 10 | } 11 | 12 | // Helper function to call the Lob API 13 | async function callLobApi(name, address, settings) { 14 | let body = { 15 | description: 'Description of the card', 16 | to: { 17 | name: name, 18 | address_line1: address_line1, 19 | address_line2: address_line2, 20 | address_city: address.city, 21 | address_state: address.state, 22 | address_zip: address.zip 23 | }, 24 | from: { 25 | name: "Segment", 26 | address_line1: "Address Line 1", 27 | address_line2: "Address Line 2", 28 | address_city: "City", 29 | address_state: "State", 30 | address_zip: "Zip" 31 | }, 32 | front: "Front HTML for {{name}}", 33 | back: "Back HTML for {{name}}", 34 | merge_variables: { 35 | name: name 36 | } 37 | } 38 | 39 | let call = await fetch(`${endpoint}`, { 40 | body: JSON.stringify(body), 41 | headers: new Headers({ 42 | 'Authorization': "Basic " + btoa(settings.apiKey + ":" + ''), 43 | "Content-Type": "application/json", 44 | }), 45 | method: "post" 46 | }) 47 | 48 | return call.json() 49 | } 50 | -------------------------------------------------------------------------------- /destinations/optimizely/Readme.md: -------------------------------------------------------------------------------- 1 | # Optimizely Custom Destination Function 2 | 3 | This function forwards events to optimizely as a `track` metric type with tags forwarded. 4 | 5 | The function adds events for both anonomous and userId. You may only want to use one of those id in your event payload. 6 | ## Settings 7 | 8 | - `sdkKey` (string) ([Optimizely SDK key](https://docs.developers.optimizely.com/full-stack/docs/initialize-sdk-javascript#section-parameters)) 9 | - `sendAnonId` (boolean) Dispatch an event to Optimizely for each Segment Track event using the Segment anonymous ID as the Optimizely visitorId if available in event payload 10 | - `sendKnownId` (boolean) Dispatch an event to Optimizely for each Segment Track event using the Segment User ID on the event as the Optimizely visitorId if available in event payload 11 | 12 | When implementing experiments with Optimizely, we recommend that you use a single type of user identifier to bucket visitors throughout. If you are using both the anonymous ID and the User ID at different points in your stack, you can enable both toggles to dispatch a distinct event to Optimizely with each id. Assuming that you are using a single ID type for any given experiment, this should not result in double counting for experiment results. It can, however result in duplicate events being generated in our event exports. 13 | -------------------------------------------------------------------------------- /destinations/optimizely/handler.js: -------------------------------------------------------------------------------- 1 | // Learn more about destination functions API at 2 | // https://segment.com/docs/connections/destinations/destination-functions 3 | 4 | /** 5 | * Handle track event 6 | * @param {SegmentTrackEvent} event 7 | * @param {FunctionSettings} settings 8 | */ 9 | async function onTrack(event, settings) { 10 | //1. Get Datafile 11 | /*var event = { 12 | "type": "track", 13 | "event": "client_event", 14 | "userId": "test-user-23js8", 15 | "timestamp": "2019-04-08T01:19:38.931Z", 16 | "anonId": 'anonymous!', 17 | "email": "test@example.com", 18 | "properties": { 19 | "plan": "Pro Annual", 20 | "accountType" : "Facebook" 21 | } 22 | };*/ 23 | 24 | var datafileId = settings.sdkKey; 25 | if (datafileId === undefined) return; 26 | 27 | var dataFileUrl = 28 | 'https://cdn.optimizely.com/datafiles/' + datafileId + '.json'; 29 | var req = await fetch(dataFileUrl); 30 | //console.log(req.body); 31 | var json = await req.json(); 32 | //2. Get Datafile Events 33 | var events = json.events; 34 | //var attributes = json.attributes; 35 | //4. Check for presence of converted string 36 | // build simply object - evntKey: evntId lookup 37 | let eventMapping = events.reduce((collection, val) => { 38 | collection[val.key] = val.id; 39 | return collection; 40 | }, {}); 41 | var eventId = eventMapping[event.event]; 42 | if (eventId === undefined) { 43 | throw new EventNotFound('Event not in datafile'); 44 | } 45 | var starting_payload = { 46 | revision: json.revision, 47 | account_id: json.accountId, 48 | visitors: [], 49 | anonymize_ip: json.anonymizeIP, 50 | client_name: 'Optimizely/segment-cloud-poc', 51 | client_version: '1.0.0', 52 | enrich_decisions: true, 53 | project_id: json.projectId 54 | }; 55 | var visitorTemplateObject = { 56 | visitor_id: '', 57 | attributes: [], 58 | snapshots: [ 59 | { 60 | decisions: [], 61 | events: [ 62 | { 63 | key: '', 64 | entity_id: '', 65 | timestamp: '', 66 | uuid: '', 67 | tags: {} 68 | } 69 | ] 70 | } 71 | ] 72 | }; 73 | //6. Build payload 74 | //7. Dispatch event 75 | //debugger; 76 | var sendUserId = settings.sendKnownId; 77 | if (sendUserId && event.userId) { 78 | var knownUser = Object.assign({}, visitorTemplateObject); 79 | knownUser.visitor_id = event.userId; 80 | if (typeof json.botFiltering === 'boolean') { 81 | knownUser.attributes.push({ 82 | entity_id: '$opt_bot_filtering', 83 | key: '$opt_bot_filtering', 84 | type: 'custom', 85 | value: json.botFiltering 86 | }); 87 | } 88 | 89 | knownUser.snapshots[0].events[0].key = event.event; 90 | knownUser.snapshots[0].events[0].entity_id = eventId; 91 | knownUser.snapshots[0].events[0].timestamp = Date.parse(event.timestamp); 92 | knownUser.snapshots[0].events[0].uuid = __uuidv4(); 93 | knownUser = __setTags(event, knownUser); 94 | starting_payload.visitors.push(knownUser); 95 | } 96 | var sendAnonymousId = settings.sendAnonId; 97 | if (sendAnonymousId && event.anonId) { 98 | var anonUser = Object.assign({}, visitorTemplateObject); 99 | anonUser.visitor_id = event.anonId; 100 | if (typeof json.botFiltering === 'boolean') { 101 | anonUser.attributes.push({ 102 | entity_id: '$opt_bot_filtering', 103 | key: '$opt_bot_filtering', 104 | type: 'custom', 105 | value: json.botFiltering 106 | }); 107 | } 108 | 109 | anonUser.snapshots[0].events[0].key = event.event; 110 | anonUser.snapshots[0].events[0].entity_id = eventId; 111 | anonUser.snapshots[0].events[0].timestamp = Date.parse(event.timestamp); 112 | anonUser.snapshots[0].events[0].uuid = __uuidv4(); 113 | anonUser = __setTags(event, anonUser); 114 | 115 | starting_payload.visitors.push(anonUser); 116 | } 117 | 118 | var body = JSON.stringify(starting_payload); 119 | console.log(body); 120 | var url = new URL('https://logx.optimizely.com/v1/events'); 121 | const res = await fetch(url.toString(), { 122 | body: body, 123 | headers: new Headers({ 124 | 'Content-Type': 'application/json' 125 | }), 126 | method: 'post' 127 | }); 128 | 129 | return await res.text(); // or res.text() to avoid parsing response as JSON 130 | } 131 | 132 | function __setTags(event, payload) { 133 | for (x in event.properties) { 134 | if (event.properties[x] === null || event.properties[x] === undefined) { 135 | continue; 136 | } 137 | 138 | if (x == 'revenue') { 139 | payload.snapshots[0].events[0].revenue = Math.round( 140 | parseFloat(event.properties[x]) * 100.0); 141 | } else if (x == 'value') { 142 | payload.snapshots[0].events[0].value = event.properties[x]; 143 | } 144 | 145 | payload.snapshots[0].events[0].tags[x] = event.properties[x]; 146 | } 147 | 148 | return payload; 149 | } 150 | /** 151 | * @description UUID generator from https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript 152 | */ 153 | function __uuidv4() { 154 | return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { 155 | var r = (Math.random() * 16) | 0, 156 | v = c == 'x' ? r : (r & 0x3) | 0x8; 157 | return v.toString(16); 158 | }); 159 | } 160 | 161 | -------------------------------------------------------------------------------- /destinations/qualtrics/handler.js: -------------------------------------------------------------------------------- 1 | //written by Vinayak, grabbed from Slack by Diggory 2 | //see required settings in "required-settings.png" in this folder 3 | 4 | // Learn more about destination functions API at 5 | // https://segment.com/docs/connections/destinations/destination-functions 6 | /** 7 | * Handle track event 8 | * @param {SegmentTrackEvent} event 9 | * @param {FunctionSettings} settings 10 | */ 11 | async function onTrack(event, settings) { 12 | // Learn more at https://segment.com/docs/connections/spec/identify/ 13 | throw new EventNotSupported('track is not supported'); 14 | } 15 | /** 16 | * Handle identify event 17 | * @param {SegmentIdentifyEvent} event 18 | * @param {FunctionSettings} settings 19 | */ 20 | async function onIdentify(event, settings) { 21 | const endpoint = `https://${settings.dataCenterId}.qualtrics.com/API/v3/directories/${settings.directoryId}/mailinglists/${settings.mailingListId}/contacts`; 22 | let fetchedUser; 23 | // fetch from profile api additional enrichhment 24 | try { 25 | fetchedUser = await fetchProfile( 26 | event.userId, 27 | settings.spaceId, 28 | settings.profileApiToken 29 | ); 30 | } catch (error) { 31 | // Retry on connection error' 32 | throw new RetryError(error.message); 33 | } 34 | let payload = { 35 | email: event.traits.email, 36 | extRef: event.userId 37 | }; 38 | let user = await fetchedUser.json(); 39 | let { firstName, lastName, shippingCountry, sellerTier } = user.traits; 40 | console.log('pulled TRAITS:', user.traits); 41 | if (firstName || lastName) { 42 | payload.firstName = firstName; 43 | payload.lastName = lastName; 44 | payload.embeddedData = { 45 | shippingCountry, 46 | sellerTier 47 | }; 48 | } 49 | console.log('final payload: ', payload); 50 | try { 51 | response = await fetch(endpoint, { 52 | method: 'POST', 53 | headers: { 54 | 'X-API-TOKEN': settings.apiToken, 55 | 'Content-Type': 'application/json' 56 | }, 57 | body: JSON.stringify(payload) 58 | }); 59 | } catch (error) { 60 | // Retry on connection error 61 | throw new RetryError(error.message); 62 | } 63 | if (response.status >= 500 || response.status === 429) { 64 | // Retry on 5xx (server errors) and 429s (rate limits) 65 | throw new RetryError(`Failed with ${response.status}`); 66 | } 67 | } 68 | /** 69 | * Handle group event 70 | * @param {SegmentGroupEvent} event 71 | * @param {FunctionSettings} settings 72 | */ 73 | async function onGroup(event, settings) { 74 | // Learn more at https://segment.com/docs/connections/spec/group/ 75 | throw new EventNotSupported('group is not supported'); 76 | } 77 | /** 78 | * Handle page event 79 | * @param {SegmentPageEvent} event 80 | * @param {FunctionSettings} settings 81 | */ 82 | async function onPage(event, settings) { 83 | // Learn more at https://segment.com/docs/connections/spec/page/ 84 | throw new EventNotSupported('page is not supported'); 85 | } 86 | /** 87 | * Handle screen event 88 | * @param {SegmentScreenEvent} event 89 | * @param {FunctionSettings} settings 90 | */ 91 | async function onScreen(event, settings) { 92 | // Learn more at https://segment.com/docs/connections/spec/screen/ 93 | throw new EventNotSupported('screen is not supported'); 94 | } 95 | /** 96 | * Handle alias event 97 | * @param {SegmentAliasEvent} event 98 | * @param {FunctionSettings} settings 99 | */ 100 | async function onAlias(event, settings) { 101 | // Learn more at https://segment.com/docs/connections/spec/alias/ 102 | throw new EventNotSupported('alias is not supported'); 103 | } 104 | /** 105 | * Handle delete event 106 | * @param {SegmentDeleteEvent} event 107 | * @param {FunctionSettings} settings 108 | */ 109 | async function onDelete(event, settings) { 110 | // Learn more at https://segment.com/docs/partners/spec/#delete 111 | throw new EventNotSupported('delete is not supported'); 112 | } 113 | async function fetchProfile(lookup_value, space_id, profile_api_token) { 114 | const base_url = `https://profiles.segment.com/v1/spaces/${space_id}/collections/users/profiles`; 115 | const url = `${base_url}/user_id:${lookup_value}/traits?limit=200`; 116 | let response; 117 | try { 118 | response = await fetch(url, { 119 | method: 'GET', 120 | headers: { 121 | Authorization: `Basic ${btoa(profile_api_token + ':')}`, 122 | 'Content-Type': 'application/json' 123 | } 124 | }); 125 | } catch (error) { 126 | // Retry on connection error 127 | throw new RetryError(error.message); 128 | } 129 | if ( 130 | response.status >= 500 || 131 | response.status == 429 || 132 | response.status == 401 133 | ) { 134 | // Retry on 5xx (server errors) and 429s (rate limits) 135 | throw new RetryError(`Failed with ${response.status}`); 136 | } 137 | if (response.status == 200) { 138 | return response; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /destinations/qualtrics/required-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segmentio/functions-library/aa49c0ef635b5f030806c7fe1a771bbe9ccdac6f/destinations/qualtrics/required-settings.png -------------------------------------------------------------------------------- /destinations/requestbin/Readme.md: -------------------------------------------------------------------------------- 1 | # Request Bin 2 | 3 | This function posts events to a "request bin" for introspection. It posts: 4 | 5 | * Track events as-is 6 | * Identify events enriched and scrubed for PII 7 | 8 | It filters all other events with demonstrative errors. -------------------------------------------------------------------------------- /destinations/requestbin/handler.js: -------------------------------------------------------------------------------- 1 | // This destinations sends data to https://requestbin.com/ for introspection 2 | // Create a request bin and update this endpoint 3 | const endpoint = new URL("https://REDACTED.x.pipedream.net") 4 | 5 | /** 6 | * onTrack takes a Track event and POSTs it to an external API with fetch() 7 | * 8 | * @param {SpecTrack} event The track event 9 | * @param {Object.} settings Custom settings 10 | * @return any 11 | */ 12 | async function onTrack(event, settings) { 13 | endpoint.searchParams.set("ts", event.timestamp) 14 | 15 | const res = await fetch(endpoint, { 16 | body: JSON.stringify(event), 17 | headers: new Headers({ 18 | "Authentication": 'Basic ' + btoa(settings.apiKey), 19 | "Content-Type": "application/json", 20 | }), 21 | method: "post", 22 | }) 23 | 24 | return await res.json() // or res.text() for non-JSON APIs 25 | } 26 | 27 | /** 28 | * onIdentify takes an Identify event, removes PII, enriches it, then POSTs it 29 | * 30 | * @param {SpecIdentify} event The identify event 31 | * @param {Object.} settings Custom settings 32 | * @return any 33 | */ 34 | async function onIdentify(event, settings) { 35 | const PII = ['ssn', 'first_name', 'last_name', 'name', 'email']; 36 | PII.forEach(i => delete event[i]); 37 | 38 | const resp = await fetch('https://reqres.in/api/users/2'); 39 | const user = await resp.json(); 40 | 41 | event.traits = event.traits || {}; 42 | event.traits.avatar = user.data && user.data.avatar; 43 | 44 | const res = await fetch(endpoint, { 45 | body: JSON.stringify(event), 46 | method: "post", 47 | }) 48 | 49 | return await res.json() 50 | } 51 | 52 | /** 53 | * onGroup demonstrates how to handle an invalid event 54 | * 55 | * @param {SpecGroup} event The group event 56 | * @param {Object.} settings Custom settings 57 | * @return any 58 | */ 59 | async function onGroup(event, settings) { 60 | if (!event.company) { 61 | throw new InvalidEventPayload("company is required") 62 | } 63 | } 64 | 65 | /** 66 | * onPage demonstrates how to handle an invalid setting 67 | * 68 | * @param {SpecPage} event The page event 69 | * @param {Object.} settings Custom settings 70 | * @return any 71 | */ 72 | async function onPage(event, settings) { 73 | if (!settings.accountId) { 74 | throw new ValidationError("Account ID is required") 75 | } 76 | } 77 | 78 | /** 79 | * onPage demonstrates how to handle an event that isn't supported 80 | * 81 | * @param {SpecAlias} event The alias event 82 | * @param {Object.} settings Custom settings 83 | * @return any 84 | */ 85 | async function onAlias(event, settings) { 86 | throw new EventNotSupported("alias not supported") 87 | } 88 | 89 | /** 90 | * onScreen demonstrates that not defining a function implies EventNotSupported 91 | * 92 | * @param {SpecScreen} event The screen event 93 | * @param {Object.} settings Custom settings 94 | * @return any 95 | */ 96 | // async function onScreen(event, settings) { 97 | // throw new EventNotSupported("screen not supported") 98 | // } 99 | -------------------------------------------------------------------------------- /destinations/salesloft/handler.js: -------------------------------------------------------------------------------- 1 | const endpoint = new URL("https://api.salesloft.com/v2/people.json") 2 | 3 | async function onIdentify(event, settings) { 4 | 5 | event.traits = event.traits || {}; 6 | 7 | if (!(event.traits["first_name"]) && !(event.traits["last_name"]) && 8 | (event.traits["name"]) && event.traits["name"].includes(" ")) { 9 | event.traits["first_name"] = event.traits["name"].split(' ')[0] 10 | event.traits["last_name"] = event.traits["name"].split(' ')[1] 11 | delete event.traits["name"] 12 | } 13 | if (event.traits["email"]) { 14 | event.traits["email_address"] = event.traits["email"] 15 | delete event.traits["email"] 16 | } 17 | if ( !(event.traits["email_address"]) && !(event.traits["last_name"] && event.traits["phone"]) ) { 18 | throw new EventNotSupported("must be created with a valid email address or partial name and phone number") 19 | } 20 | 21 | const res = await fetch(endpoint, { 22 | 23 | body: JSON.stringify(event.traits), 24 | headers: new Headers({ 25 | "Authorization": 'Bearer '+settings.apiKey, 26 | "Content-Type": "application/json", 27 | }), 28 | method: "post" 29 | }) 30 | return await res.json() 31 | } 32 | -------------------------------------------------------------------------------- /destinations/sift/README.md: -------------------------------------------------------------------------------- 1 | # Sift Custom Destination Function 2 | This example shows how to set up a custom destination function with [Sift](https://sift.com/), a solution that helps you prevent all types of online fraud and abuse. The destination not only sends events to Sift, but also takes the results from Sift and attaches the metric to a user's profile using the `identify` call. 3 | 4 | ## Setup 5 | - [ ] Create a [HTTP Source](https://segment.com/docs/connections/sources/catalog/libraries/server/http-api/#http%20tracking%20api) 6 | - [ ] Grab the REST API Key from Sift in your account 7 | 8 | ## How to use 9 | 10 | Copy the `handler.js` code in this directory to your destination function 11 | 12 | Make the necessary modifications in the `handler.js` to map your Segment data. 13 | 14 | Replace the first line with your Segment write key that you generated from creating an HTTP Source 15 | 16 | ## Maintainers 17 | [@shyaankhan](https://github.com/shyaankhan) Segment 18 | [@ssaouma](https://github.com/ssaouma) Sift 19 | [@juhaelee](https://github.com/juhaelee) Segment -------------------------------------------------------------------------------- /destinations/sift/handler.js: -------------------------------------------------------------------------------- 1 | // Segment & Sift 2 | const SEGMENT_WRITE_KEY = ''; 3 | const siftEndpoint = 'https://api.sift.com/v205/events'; 4 | const endpointScore = siftEndpoint + '?return_score=true'; 5 | const endpointWorkflow = siftEndpoint + '?return_workflow_status=true'; 6 | 7 | async function addScores(fields, res) { 8 | if (res.status == 0) { 9 | var scoreBody = { 10 | userId: fields.$user_id, 11 | traits: { 12 | contentAbuseScore: res.score_response.scores.content_abuse.score, 13 | paymentAbuseScore: res.score_response.scores.payment_abuse.score 14 | } 15 | }; 16 | 17 | const request = await fetch('https://api.segment.io/v1/identify', { 18 | body: JSON.stringify(scoreBody), 19 | headers: new Headers({ 20 | Authorization: 'Basic ' + btoa(SEGMENT_WRITE_KEY + ':'), 21 | 'Content-Type': 'application/json' 22 | }), 23 | method: 'post' 24 | }); 25 | 26 | return request.json(); 27 | } 28 | } 29 | 30 | async function addDecisions(fields, res) { 31 | var decisionBody = { 32 | userId: fields.$user_id, 33 | traits: { 34 | contentAbuseDecisions: 35 | res.score_response.workflow_statuses[0].history[0].config.decision_id, 36 | paymentAbuseDecisions: 37 | res.score_response.workflow_statuses[0].history[0].config.decision_id 38 | } 39 | }; 40 | 41 | const request = await fetch('https://api.segment.io/v1/identify', { 42 | body: JSON.stringify(decisionBody), 43 | headers: new Headers({ 44 | Authorization: 'Basic ' + btoa(SEGMENT_WRITE_KEY + ':'), 45 | 'Content-Type': 'application/json' 46 | }), 47 | method: 'post' 48 | }); 49 | 50 | return request.json(); 51 | } 52 | 53 | function getUrl(type) { 54 | if (type == 'SCORE') { 55 | return endpointScore; 56 | } else if (type == 'WORKFLOW') { 57 | return endpointWorkflow; 58 | } else { 59 | return siftEndpoint; 60 | } 61 | } 62 | 63 | async function siftEventCall(fields, type) { 64 | const res = await fetch(getUrl(type), { 65 | body: JSON.stringify(fields), 66 | headers: { 'Content-Type': 'application/json' }, 67 | method: 'post' 68 | }); 69 | 70 | const siftResponse = await res.json(); 71 | 72 | if (siftResponse.status <= 0) { 73 | // Please implement conditions for retries. 74 | } else if (siftResponse.status >= 0) { 75 | throw new InvalidEventPayload(siftResponse.error_message); 76 | } 77 | 78 | var response; 79 | 80 | if (type == 'SCORE') { 81 | response = await addScores(fields, siftResponse); 82 | } else if (type == 'WORKFLOW') { 83 | response = await addDecisions(fields, siftResponse); 84 | } 85 | 86 | return response; 87 | } 88 | 89 | async function onTrack(event, settings) { 90 | var fields = {}; 91 | // Depending on when you want to call for a score, set the appropriate endpoint to hit. 92 | if (event.event == 'Signed Up') { 93 | fields = { 94 | $type: '$create_account', 95 | $user_id: event.userId, 96 | $name: event.properties.name, 97 | $user_email: event.properties.email, 98 | $ip: event.context.ip, 99 | $phone: event.properties.phone, 100 | $browser: { 101 | $user_agent: event.context.userAgent 102 | }, 103 | $api_key: settings.apiKey 104 | }; 105 | 106 | return siftEventCall(fields, 'REGULAR'); 107 | } else if (event.event == 'Signed In') { 108 | fields = { 109 | $type: '$login', 110 | $login_status: '$success', 111 | $user_id: event.userId, 112 | $username: event.properties.username, 113 | $ip: event.context.ip, 114 | $browser: { 115 | $user_agent: event.context.userAgent 116 | }, 117 | $api_key: settings.apiKey 118 | }; 119 | 120 | return siftEventCall(fields, 'REGULAR'); 121 | } else { 122 | return null; 123 | } 124 | } 125 | 126 | async function onIdentify(event, settings) { 127 | // Depending on when you want to call for a score, set the appropriate endpoint to hit. 128 | 129 | if (!event.userId) return; 130 | 131 | var fields = { 132 | $type: '$update_account', 133 | $user_id: event.userId, 134 | $name: event.traits.name, 135 | $user_email: event.traits.email, 136 | $ip: event.context.ip, 137 | $phone: event.traits.phone, 138 | $browser: { 139 | $user_agent: event.context.userAgent 140 | }, 141 | $api_key: settings.apiKey 142 | }; 143 | 144 | return siftEventCall(fields, endpoint); 145 | } 146 | -------------------------------------------------------------------------------- /destinations/slack/Readme.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segmentio/functions-library/aa49c0ef635b5f030806c7fe1a771bbe9ccdac6f/destinations/slack/Readme.md -------------------------------------------------------------------------------- /destinations/slack/handler.js: -------------------------------------------------------------------------------- 1 | // This example uses a Slack incoming webhook (https://api.slack.com/incoming-webhooks) 2 | // Create a Slack app and webhook and update this endpoint 3 | const endpoint = "https://hooks.slack.com/services/REDACTED" 4 | 5 | async function onTrack(event, settings) { 6 | if (!event.email) { 7 | throw new InvalidEventPayload("email is required") 8 | } 9 | 10 | const hash = crypto.createHash('md5').update(event.email).digest("hex"); 11 | 12 | const res = await fetch(endpoint, { 13 | body: JSON.stringify({ 14 | "blocks": [ 15 | { 16 | "type": "section", 17 | "text": { 18 | "type": "mrkdwn", 19 | "text": `${event.email} ${event.event.toLowerCase()} for a *${event.properties.plan}* plan` 20 | }, 21 | "accessory": { 22 | "type": "image", 23 | "image_url": `https://www.gravatar.com/avatar/${hash}`, 24 | "alt_text": event.email, 25 | } 26 | } 27 | ] 28 | }), 29 | method: "post", 30 | }) 31 | 32 | return await res.text() 33 | } 34 | -------------------------------------------------------------------------------- /destinations/wavefront/README.md: -------------------------------------------------------------------------------- 1 | # PURPOSE 2 | This custom destination allows you to send user events to Wavefront's events [system](https://docs.wavefront.com/events.html). This allows you to correlate user events with other system or app metrics and help troubleshoot issues faster. 3 | 4 | # What do you need? 5 | 6 | 1. Update API key in settings of custom function 7 | 2. Update wavefrontInstance on Line 7 to your own 8 | 3. Save and Enjoy :) -------------------------------------------------------------------------------- /destinations/wavefront/handler.js: -------------------------------------------------------------------------------- 1 | 2 | // This example hits the WAVEFRONT REST API V2 events endpoint 3 | // Note 1: The wavefront instance name is different for each customer 4 | // Note 2: v2 is the only supported version as of 1/15/2020 5 | 6 | //TODO: Move these to settings once more than apiKey is supported 7 | let wavefrontInstance = "longboard"; 8 | let wavefrontApiVersion = "v2"; 9 | let wavefrontResource = "event"; 10 | 11 | //Create endpoint 12 | const url = new URL(`https://${wavefrontInstance}.wavefront.com/api/${wavefrontApiVersion}/${wavefrontResource}`); 13 | 14 | //Properties we need in the track event 15 | //Note 1: we only cover instantaneous events for now as ending events later is only possible via wavefront UI manually 16 | //Note 2: startTime and endTime are just set to timestamp 17 | //TODO: Convert some important context properties to tags 18 | const annotationProperties = [ 19 | { key: "type", validateFunc: (input) => { return _.isString(input)} }, 20 | { key: "details", validateFunc: (input) => { return _.isString(input)} }, 21 | { key: "severity", validateFunc: (input) => { return _.isString(input) && _.indexOf(["info", "warn", "severe"], input) > -1 } }]; 22 | 23 | /** 24 | * @param {SpecTrack} event The track event 25 | * @param {Object.} settings Custom settings 26 | * @return void 27 | */ 28 | async function onTrack(event, settings) { 29 | let { properties, timestamp } = event; 30 | 31 | let eventName = event.event; 32 | let timestampInSeconds = Date.parse(timestamp); 33 | let annotations = { 34 | prettyName: eventName 35 | }; 36 | annotationProperties.forEach(subproperty => { 37 | let { key, validateFunc } = subproperty; 38 | if(properties.hasOwnProperty(key) && validateFunc(properties[key])) 39 | annotations[key] = properties[key]; 40 | }); 41 | 42 | let body = { 43 | name: eventName, 44 | startTime: timestampInSeconds, 45 | endTime: timestampInSeconds + 1, 46 | annotations 47 | }; 48 | 49 | const res = await fetch(url, { 50 | body: JSON.stringify(body), 51 | headers: new Headers({ 52 | "Authorization": `Bearer ${settings.apiKey}`, 53 | "Content-Type": "application/json", 54 | "Accept": "application/json" 55 | }), 56 | method: "post", 57 | }) 58 | 59 | //TODO: there are probably other error types 60 | //Note 1: API sometimes returns 200 even when errors occur 61 | let response = await res.json(); 62 | let { message, code, error, status={} } = response; 63 | 64 | if(status.hasOwnProperty("code") && status.code === 200 && status.hasOwnProperty("result") && status.result === "OK") 65 | return response; 66 | else if(code === 400 && !!message) 67 | throw new ValidationError(message); 68 | else if((status.hasOwnProperty("result") && status.result === "ERROR") || !!error) 69 | throw new InvalidEventPayload(message); 70 | else 71 | throw new EventNotSupported("Not sure what is going on"); 72 | } 73 | 74 | /** 75 | * onIdentify takes an Identify event, but wavefront events have no concept of user info 76 | * 77 | * @param {SpecIdentify} event The identify event 78 | * @param {Object.} settings Custom settings 79 | * @return any 80 | */ 81 | async function onIdentify(event, settings) { 82 | throw new EventNotSupported("alias not supported") 83 | } 84 | 85 | /** 86 | * onGroup not supported 87 | * 88 | * @param {SpecGroup} event The group event 89 | * @param {Object.} settings Custom settings 90 | * @return any 91 | */ 92 | async function onGroup(event, settings) { 93 | throw new EventNotSupported("alias not supported") 94 | } 95 | 96 | /** 97 | * onPage not supported 98 | * 99 | * @param {SpecPage} event The page event 100 | * @param {Object.} settings Custom settings 101 | * @return any 102 | */ 103 | // page demonstrates how to handle an invalid setting 104 | //TODO: doesn't this get covered by onTrack? 105 | async function onPage(event, settings) { 106 | throw new EventNotSupported("page not supported") 107 | } 108 | 109 | /** 110 | * onAlias not supported 111 | * 112 | * @param {SpecAlias} event The alias event 113 | * @param {Object.} settings Custom settings 114 | * @return any 115 | */ 116 | async function onAlias(event, settings) { 117 | throw new EventNotSupported("alias not supported") 118 | } 119 | 120 | 121 | /** 122 | * onScreen not supported 123 | * 124 | * @param {SpecScreen} event The screen event 125 | * @param {Object.} settings Custom settings 126 | * @return any 127 | */ 128 | //TODO: doesn't this get covered by onTrack? 129 | async function onScreen(event, settings) { 130 | throw new EventNotSupported("screen not supported") 131 | } 132 | -------------------------------------------------------------------------------- /destinations/zendesk/handler.js: -------------------------------------------------------------------------------- 1 | async function onTrack(event, settings) { 2 | const endpoint = `https://${settings.subdomain}.zendesk.com/api/v2/tickets.json` 3 | 4 | if (event.event = "Application Submitted") { 5 | 6 | const newTicket = { 7 | "ticket": { 8 | "subject": `Application Review: ${event.properties.display_name}`, 9 | "comment": { 10 | "body": `Type: ${event.properties.component_type} 11 | App ID: ${event.properties.app_name} 12 | UserID: ${event.userId} 13 | Include steps here for what your Support Team to action on when seeing this application submitted`, 14 | // This ensures that this is created as in internal note in Zendesk 15 | "public": false 16 | }, 17 | "priority": "high", 18 | "tags": ["application-support"] 19 | } 20 | } 21 | 22 | const res = await fetch(endpoint, { 23 | body: JSON.stringify(newTicket), 24 | headers: { 25 | // https://developer.zendesk.com/rest_api/docs/support/introduction#basic-authentication 26 | "Authorization": `Basic ${settings.credentials}`, 27 | "Content-Type": "application/json" 28 | }, 29 | method: "post" 30 | }) 31 | 32 | return res.json() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /destinations/zendesk/readme.md: -------------------------------------------------------------------------------- 1 | # Zendesk Destination Function 2 | 3 | This function utilizes Zendesk's [Tickets API](https://developer.zendesk.com/rest_api/docs/support/tickets) to create a new ticket with an internal note for your Support team to action on each time a user submits an application for review where Segment's [Track method](https://segment.com/docs/spec/track/) is called. 4 | 5 | 6 | ## Getting Started 7 | 1. In the Settings Builder, make sure you add the relevant [Settings as listed below](#settings), making sure the naming matches what's in the code on lines 2 and 26. You can find the Settings Builder under App Info > Settings (left hand nav). 8 | 2. Copy and paste the Zendesk function template code in the `index.js` file directly into the editor. 9 | 3. Add your relevant Zendesk [Subdomain](https://support.zendesk.com/hc/en-us/articles/221682747-Where-can-I-find-my-Zendesk-subdomain-) and [credentials](https://developer.zendesk.com/rest_api/docs/support/introduction#basic-authentication) so you can send a test event. 10 | 4. You can send the following test event to validate the existing template works as expected and see the feedback populate in your table: 11 | ``` 12 | { 13 | "event": "Application Submitted", 14 | "originalTimestamp": "2019-07-19T18:49:41.814249419Z", 15 | "properties": { 16 | "app_name": "apps/195", 17 | "component_type": "subscription", 18 | "display_name": "Customer X's Application" 19 | }, 20 | "receivedAt": "2019-07-19T18:49:46.214Z", 21 | "sentAt": "2019-07-19T18:49:46.205Z", 22 | "timestamp": "2019-07-19T18:49:41.823Z", 23 | "type": "track", 24 | "userId": "userId123" 25 | } 26 | ``` 27 | 5. You're done! Feel free to modify the template to suit your organization's specific needs. 28 | 29 | 30 | ## Settings 31 | 32 | - `subdomain` (string) 33 | - `credentials` (string) 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "functions-library", 3 | "version": "1.0.0", 4 | "description": "This repository contains a set of community-generated functions, to serve as examples to base your own functions upon. If you're building out a new integration, or a custom piece of code that you want Segment to run, use this repo as the set of examples.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "jest --verbose" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/segmentio/functions-library.git" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "ISC", 16 | "bugs": { 17 | "url": "https://github.com/segmentio/functions-library/issues" 18 | }, 19 | "homepage": "https://github.com/segmentio/functions-library#readme", 20 | "dependencies": { 21 | "atob": "2.1.2", 22 | "aws-sdk": "2.488.0", 23 | "btoa": "1.2.1", 24 | "form-data": "2.4.0", 25 | "lodash": "4.17.15", 26 | "node-fetch": "2.6.0", 27 | "oauth": "0.9.15", 28 | "xml": "1.0.1" 29 | }, 30 | "devDependencies": { 31 | "jest": "^24.9.0", 32 | "nock": "^11.3.5" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /sources.test.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | const process = require('process') 3 | const { processSourcePayload } = require('./buildpack/boreal') 4 | const { EventNotSupported, InvalidEventPayload, ValidationError } = require('./buildpack/boreal/window') 5 | 6 | const sources = fs.readdirSync(`${__dirname}/sources`) 7 | const skips = ["leanplum"] 8 | 9 | describe.each(sources)("%s", (source) => { 10 | let dir = `${__dirname}/sources/${source}` 11 | let payloads = [] 12 | 13 | let examples 14 | try { 15 | examples = fs.readdirSync(`${dir}/webhook-examples`)} 16 | catch(e) { 17 | console.log(`no tests for ${source}, skipping`) 18 | return 19 | } 20 | for (var i = 0; i < examples.length; i++) { 21 | const example = examples[i] 22 | const payload = JSON.parse(fs.readFileSync(`${dir}/webhook-examples/${example}`, 'utf8')) 23 | payloads.push([example, payload]) 24 | } 25 | 26 | let tester = test 27 | if (skips.indexOf(source) > -1) { 28 | tester = xtest 29 | } 30 | 31 | tester.each(payloads)("%s payload", async (example, payload) => { 32 | expect(payload.payload.body).toBeDefined() 33 | }) 34 | 35 | tester.each(payloads)("%s handler", async (example, payload) => { 36 | process.chdir(dir) 37 | try { 38 | const messages = await processSourcePayload(payload) 39 | expect(messages.events.length + messages.objects.length).toBeGreaterThanOrEqual(0) 40 | } catch(err) { 41 | if (!(err instanceof EventNotSupported || err instanceof ValidationError || err instanceof InvalidEventPayload)) { 42 | fail(err) 43 | } 44 | } 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /sources/Yotpo/handler.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Subscribes to Yotpo webhooks and creates 'Product Reviewed' Segment Track events 4 | * Yotpo webhooks: https://apidocs.yotpo.com/reference#introduction-to-webhooks 5 | * 6 | */ 7 | async function onRequest(request, settings) { 8 | const body = request.json(); 9 | if (!validate(body)) { 10 | throw new EventNotSupported(`${request.event} is not supported.`); 11 | } 12 | 13 | // retreive metadata from Yotpo 14 | const review = body.data; 15 | 16 | // create anonymousId that will be consistent with subsequent webhook events from same user 17 | const hash = crypto.createHash('md5'); 18 | hash.update(review.customer_email); 19 | const anonymousId = hash.digest('hex'); 20 | 21 | // Segment Track call matching 'Product Reviewed' spec - See https://segment.com/docs/connections/spec/ecommerce/v2/#product-reviewed 22 | Segment.track({ 23 | event: 'Product Reviewed', 24 | anonymousId: anonymousId, 25 | context: { traits: { email: review.customer_email } }, 26 | properties: { 27 | product_id: review.sku, 28 | email: review.customer_email, 29 | title: review.title, 30 | content: review.content, 31 | order_id: review.external_order_id, 32 | score: review.score, 33 | sentiment: review.sentiment, 34 | shop_owner: review.shop_owner, 35 | user_id: review.user_id, 36 | verified_buyer: review.verified_buyer, 37 | votes_down: review.votes_down, 38 | votes_up: review.votes_up 39 | } 40 | }); 41 | 42 | return { status: 'success' }; 43 | } 44 | 45 | // only send in 'review_create' events 46 | function validate({ event }) { 47 | return event === 'review_create'; 48 | } 49 | -------------------------------------------------------------------------------- /sources/Yotpo/readme.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | This is a source function template that subscribes to new product reviews via Yotpo Webhooks. 4 | 5 | -------------------------------------------------------------------------------- /sources/adyen/handler.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Please do not delete [used for Intellisense] 3 | * @param {ServerRequest} request The incoming webhook request 4 | * @param {Object.} settings Custom settings 5 | * @return void 6 | */ 7 | async function onRequest(request, settings) { 8 | let batch = request.json() 9 | 10 | for (var i in batch["notificationItems"]) { 11 | eventInput = batch["notificationItems"][i]["NotificationRequestItem"] 12 | eventOutput = {properties: {}} 13 | 14 | for (var key in batch["notificationItems"][i]["NotificationRequestItem"]) { 15 | if ("additionalData" in eventInput && "shopperReference" in eventInput["additionalData"]) { 16 | eventOutput["userId"] = eventInput["additionalData"]["shopperReference"] 17 | } else {eventOutput["userId"] = "adyen"} 18 | 19 | if (key=="eventCode") { //handle event name 20 | eventOutput["event"] = eventInput["eventCode"] 21 | } else {eventOutput["properties"][key] = eventInput[key]} 22 | 23 | if (key=="eventDate") {eventOutput["timestamp"] = eventInput["eventDate"]} //handle timestamp if present 24 | 25 | } 26 | Segment.track(eventOutput) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /sources/adyen/webhook-examples/authorisation.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "live": "false", 5 | "notificationItems": [ 6 | { 7 | "NotificationRequestItem": { 8 | "additionalData": { 9 | "expiryDate": "12/2012", 10 | " NAME1 ": "VALUE1", 11 | "cardSummary": "7777", 12 | "shopperIP": "127.0.0.1", 13 | "totalFraudScore": "10", 14 | "NAME2": " VALUE2 ", 15 | "fraudCheck-6-ShopperIpUsage": "10" 16 | }, 17 | "amount": { 18 | "currency": "EUR", 19 | "value": 10150 20 | }, 21 | "eventCode": "AUTHORISATION", 22 | "eventDate": "2019-06-27T19:10:58+02:00", 23 | "merchantAccountCode": "Diggory01COM", 24 | "merchantReference": "8313842560770001", 25 | "paymentMethod": "visa", 26 | "pspReference": "test_AUTHORISATION_3", 27 | "reason": "REFUSED", 28 | "success": "false" 29 | } 30 | } 31 | ] 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /sources/adyen/webhook-examples/orderOpened.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "live": "false", 5 | "notificationItems": [ 6 | { 7 | "NotificationRequestItem": { 8 | "amount": { 9 | "currency": "EUR", 10 | "value": 1010 11 | }, 12 | "eventCode": "ORDER_OPENED", 13 | "eventDate": "2019-06-27T19:10:58+02:00", 14 | "merchantAccountCode": "Diggory01COM", 15 | "merchantReference": "testMerchantRef1", 16 | "pspReference": "test_ORDER_OPENED", 17 | "reason": "", 18 | "success": "true" 19 | } 20 | } 21 | ] 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /sources/adyen/webhook-examples/refundWithData.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "live": "false", 5 | "notificationItems": [ 6 | { 7 | "NotificationRequestItem": { 8 | "amount": { 9 | "currency": "EUR", 10 | "value": 0 11 | }, 12 | "eventCode": "REFUND_WITH_DATA", 13 | "eventDate": "2019-06-27T19:10:58+02:00", 14 | "merchantAccountCode": "Diggory01COM", 15 | "merchantReference": "testMerchantRef1", 16 | "originalReference": "pspReference of the REFUND_WITH_DATA", 17 | "pspReference": "test_REFUND_WITH_DATA", 18 | "reason": "", 19 | "success": "true" 20 | } 21 | } 22 | ] 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /sources/audit-forwarding/handler.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Please do not delete [used for Intellisense] 3 | * @param {ServerRequest} request The incoming webhook request 4 | * @param {Object.} settings Custom settings 5 | * @return void 6 | */ 7 | async function onRequest(request, settings) { 8 | if (!settings.workspaceToken) { 9 | throw new ValidationError("Workspace Authorization Token is Required. Make sure you have created a 'workspaceToken' setting."); 10 | } 11 | if (!settings.workspaceSlug) { 12 | throw new ValidationError("Workspace Slug is Required. Make sure you have created a 'workspaceSlug' setting.") 13 | } 14 | 15 | const requestBody = request.json(); 16 | const usersURL = `https://platform.segmentapis.com/v1beta/workspaces/${settings.workspaceSlug}/users`; 17 | const userId = requestBody.properties.details.subject && requestBody.properties.details.subject.indexOf('users/') > -1 ? requestBody.properties.details.subject.split('/')[1] : requestBody.userId; 18 | 19 | // Uncomment the following 3 lines in order to block "Permission Check" events. 20 | // if (requestBody.properties.type === 'Permission Check') { 21 | // return; 22 | // } 23 | 24 | if (userId === "__system__") { 25 | Segment.track({ 26 | userId, 27 | event: requestBody.properties.type, 28 | properties: requestBody.properties 29 | }); 30 | return; 31 | } 32 | 33 | const res = await fetch(usersURL, { 34 | method: 'get', 35 | headers: { 36 | 'Content-Type': 'application/json', 37 | 'Authorization': `Bearer ${settings.workspaceToken}`, 38 | 'cache-control': 'no-cache' 39 | }, 40 | }) 41 | .then(res => res.json()) 42 | .then(json => Segment.track({ 43 | userId, 44 | event: requestBody.properties.type, 45 | properties: Object.assign({email: json.users.filter(user => { 46 | if (user.name.split('/')[1] === userId) { 47 | return user; 48 | } 49 | })[0].email}, requestBody.properties) 50 | })) 51 | .catch(err => Segment.track({ 52 | userId, 53 | event:'Error: User Email Not Found', 54 | properties: err 55 | }));; 56 | } -------------------------------------------------------------------------------- /sources/audit-forwarding/webhook-examples/audience*.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "channel": "server", 5 | "context": { 6 | "library": { 7 | "name": "unknown", 8 | "version": "unknown" 9 | } 10 | }, 11 | "event": "Audit", 12 | "integrations": {}, 13 | "messageId": "api-1U3E4UE7HqqBqUdl3oaR0YRCPCR", 14 | "originalTimestamp": "2019-11-24T06:24:46.877Z", 15 | "properties": { 16 | "details": { 17 | "resource_id": "spaces/default/audiences/aud_1U3E4PKTj08xZcWmTkQpvvTlI2V", 18 | "resource_type": "audience", 19 | "subject": "users/MNxxtIRCbe", 20 | "target": "order completed" 21 | }, 22 | "type": "Audience Created", 23 | "workspace_id": "VWDxzzIpI5" 24 | }, 25 | "receivedAt": "2019-11-24T06:24:46.896Z", 26 | "replay": true, 27 | "timestamp": "2019-11-24T06:24:46.877Z", 28 | "type": "track", 29 | "userId": "MNxxtIRCbe" 30 | }, 31 | "headers": { 32 | "Accept-Encoding": [ 33 | "gzip, deflate", 34 | "identity" 35 | ], 36 | "Content-Length": [ 37 | "575" 38 | ], 39 | "Content-Type": [ 40 | "application/json" 41 | ], 42 | "Date": [ 43 | "Sun, 24 Nov 2019 19:27:41 GMT" 44 | ], 45 | "User-Agent": [ 46 | "Segment.io/1.0" 47 | ], 48 | "X-Request-Id": [ 49 | "ff93ce17-ac05-48d4-8bec-05b8e2be4016" 50 | ] 51 | }, 52 | "queryParameters": {} 53 | } 54 | } -------------------------------------------------------------------------------- /sources/audit-forwarding/webhook-examples/computedTrait*.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "channel": "server", 5 | "context": { 6 | "library": { 7 | "name": "unknown", 8 | "version": "unknown" 9 | } 10 | }, 11 | "event": "Audit", 12 | "integrations": {}, 13 | "messageId": "api-1U4mTc9ZEtxDQFd8ev0MFf2JcDz", 14 | "originalTimestamp": "2019-11-24T19:37:30.179Z", 15 | "projectId": "B4fc7r2vDB", 16 | "properties": { 17 | "details": { 18 | "resource_id": "spaces/default/traits/tra_1U3BEICYoNvLBLTpMs8TRoXL8QH", 19 | "resource_type": "computed_trait", 20 | "subject": "users/MNxxtIRCbe", 21 | "target": "Test Computed Trait" 22 | }, 23 | "type": "Computed Trait Modified", 24 | "workspace_id": "VWDxzzIpI5" 25 | }, 26 | "receivedAt": "2019-11-24T19:37:30.203Z", 27 | "timestamp": "2019-11-24T19:37:30.179Z", 28 | "type": "track", 29 | "userId": "MNxxtIRCbe", 30 | "version": 2 31 | }, 32 | "headers": { 33 | "Accept-Encoding": [ 34 | "gzip, deflate", 35 | "identity" 36 | ], 37 | "Content-Length": [ 38 | "612" 39 | ], 40 | "Content-Type": [ 41 | "application/json" 42 | ], 43 | "Date": [ 44 | "Sun, 24 Nov 2019 19:38:31 GMT" 45 | ], 46 | "User-Agent": [ 47 | "Segment.io/1.0" 48 | ], 49 | "X-Request-Id": [ 50 | "74e2bfcb-8575-4e8d-86df-713ab0f009c4" 51 | ] 52 | }, 53 | "queryParameters": {} 54 | } 55 | } -------------------------------------------------------------------------------- /sources/audit-forwarding/webhook-examples/destinationFilter*.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "channel": "server", 5 | "context": { 6 | "library": { 7 | "name": "unknown", 8 | "version": "unknown" 9 | } 10 | }, 11 | "event": "Audit", 12 | "integrations": {}, 13 | "messageId": "api-1U3H0fs1YbtTTLBrmwZdpy1Ccqn", 14 | "originalTimestamp": "2019-11-24T06:48:56Z", 15 | "properties": { 16 | "details": { 17 | "resource_id": "workspaces/james9446/sources/audit_forwarding/destinations/webhooks/filters/df_1U0MEMv1mox9rloD2Jq2sc2Y2qW", 18 | "resource_type": "destination_filter", 19 | "subject": "users/MNxxtIRCbe", 20 | "target": "Drop \"Permission Check\" Audit Events " 21 | }, 22 | "type": "Destination Filter Disabled", 23 | "workspace_id": "VWDxzzIpI5" 24 | }, 25 | "receivedAt": "2019-11-24T06:48:56.151Z", 26 | "replay": true, 27 | "timestamp": "2019-11-24T06:48:56.000Z", 28 | "type": "track", 29 | "userId": "MNxxtIRCbe" 30 | }, 31 | "headers": { 32 | "Accept-Encoding": [ 33 | "gzip, deflate", 34 | "identity" 35 | ], 36 | "Content-Length": [ 37 | "666" 38 | ], 39 | "Content-Type": [ 40 | "application/json" 41 | ], 42 | "Date": [ 43 | "Sun, 24 Nov 2019 19:33:08 GMT" 44 | ], 45 | "User-Agent": [ 46 | "Segment.io/1.0" 47 | ], 48 | "X-Request-Id": [ 49 | "d12a6c7e-20eb-47b9-82db-d287d2a394b6" 50 | ] 51 | }, 52 | "queryParameters": {} 53 | } 54 | } -------------------------------------------------------------------------------- /sources/audit-forwarding/webhook-examples/integration*.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "channel": "server", 5 | "context": { 6 | "library": { 7 | "name": "unknown", 8 | "version": "unknown" 9 | } 10 | }, 11 | "event": "Audit", 12 | "integrations": {}, 13 | "messageId": "api-1U3DGP2HEXHV40r2hvn9PGC8XlE", 14 | "originalTimestamp": "2019-11-24T06:18:07.896Z", 15 | "properties": { 16 | "details": { 17 | "metadata_id": "56748689e954a874ca44ccfb", 18 | "resource_id": "5dd8dc3618a12651f66fb8ed", 19 | "resource_type": "integration", 20 | "source_id": "LAn9KpdvNZ", 21 | "subject": "users/MNxxtIRCbe", 22 | "target": "Audit Function - Slack" 23 | }, 24 | "type": "Integration Modified", 25 | "workspace_id": "VWDxzzIpI5" 26 | }, 27 | "receivedAt": "2019-11-24T06:18:07.918Z", 28 | "replay": true, 29 | "timestamp": "2019-11-24T06:18:07.896Z", 30 | "type": "track", 31 | "userId": "MNxxtIRCbe" 32 | }, 33 | "headers": { 34 | "Accept-Encoding": [ 35 | "gzip, deflate", 36 | "identity" 37 | ], 38 | "Content-Length": [ 39 | "623" 40 | ], 41 | "Content-Type": [ 42 | "application/json" 43 | ], 44 | "Date": [ 45 | "Sun, 24 Nov 2019 18:11:33 GMT" 46 | ], 47 | "User-Agent": [ 48 | "Segment.io/1.0" 49 | ], 50 | "X-Request-Id": [ 51 | "7a0cee2e-a48e-4e14-add2-b12043692088" 52 | ] 53 | }, 54 | "queryParameters": {} 55 | } 56 | } -------------------------------------------------------------------------------- /sources/audit-forwarding/webhook-examples/newEventAllowed.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "channel": "server", 5 | "context": { 6 | "library": { 7 | "name": "unknown", 8 | "version": "unknown" 9 | } 10 | }, 11 | "event": "Audit", 12 | "integrations": {}, 13 | "messageId": "api-1U3DHtHn7z6VEIpuTybfT5UCr0d", 14 | "originalTimestamp": "2019-11-24T06:18:20Z", 15 | "properties": { 16 | "details": { 17 | "blocked": "false", 18 | "message_id": "00645569-2bb7-42cf-8c64-df827c790773", 19 | "name": "Space Modified", 20 | "planned": "false", 21 | "resource_id": "LAn9KpdvNZ", 22 | "resource_type": "source", 23 | "source_id": "LAn9KpdvNZ", 24 | "source_name": "Audit Function", 25 | "source_slug": "audit_function", 26 | "target": "Audit Function", 27 | "tracking_plan_connected": "false", 28 | "tracking_plan_id": "", 29 | "type": "track" 30 | }, 31 | "type": "New Event Allowed", 32 | "workspace_id": "VWDxzzIpI5" 33 | }, 34 | "receivedAt": "2019-11-24T06:18:20.587Z", 35 | "replay": true, 36 | "timestamp": "2019-11-24T06:18:20.000Z", 37 | "type": "track", 38 | "userId": "__system__" 39 | }, 40 | "headers": { 41 | "Accept-Encoding": [ 42 | "gzip, deflate", 43 | "identity" 44 | ], 45 | "Content-Length": [ 46 | "764" 47 | ], 48 | "Content-Type": [ 49 | "application/json" 50 | ], 51 | "Date": [ 52 | "Sun, 24 Nov 2019 19:26:13 GMT" 53 | ], 54 | "User-Agent": [ 55 | "Segment.io/1.0" 56 | ], 57 | "X-Request-Id": [ 58 | "df5c43fc-1cf5-465d-ac09-b5135fef751b" 59 | ] 60 | }, 61 | "queryParameters": {} 62 | } 63 | } -------------------------------------------------------------------------------- /sources/audit-forwarding/webhook-examples/permissionCheck.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "channel": "server", 5 | "context": { 6 | "library": { 7 | "name": "unknown", 8 | "version": "unknown" 9 | } 10 | }, 11 | "event": "Audit", 12 | "integrations": {}, 13 | "messageId": "api-1U3E7BKLqjyM48z9ShAXcYfasab", 14 | "originalTimestamp": "2019-11-24T06:25:08Z", 15 | "properties": { 16 | "details": { 17 | "action": "source:debugger-view", 18 | "resource_id": "B4fc7r2vDB", 19 | "resource_type": "source", 20 | "sso_connection_id": "", 21 | "subject_id": "MNxxtIRCbe", 22 | "subject_type": "user" 23 | }, 24 | "type": "Permission Check", 25 | "workspace_id": "VWDxzzIpI5" 26 | }, 27 | "receivedAt": "2019-11-24T06:25:08.179Z", 28 | "replay": true, 29 | "timestamp": "2019-11-24T06:25:08.000Z", 30 | "type": "track", 31 | "userId": "MNxxtIRCbe" 32 | }, 33 | "headers": { 34 | "Accept-Encoding": [ 35 | "gzip, deflate", 36 | "identity" 37 | ], 38 | "Content-Length": [ 39 | "570" 40 | ], 41 | "Content-Type": [ 42 | "application/json" 43 | ], 44 | "Date": [ 45 | "Sun, 24 Nov 2019 19:29:31 GMT" 46 | ], 47 | "User-Agent": [ 48 | "Segment.io/1.0" 49 | ], 50 | "X-Request-Id": [ 51 | "c5aa4942-372f-4dae-ba26-b05483eedecc" 52 | ] 53 | }, 54 | "queryParameters": {} 55 | } 56 | } -------------------------------------------------------------------------------- /sources/audit-forwarding/webhook-examples/personasWarehouse*.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "channel": "server", 5 | "context": { 6 | "library": { 7 | "name": "unknown", 8 | "version": "unknown" 9 | } 10 | }, 11 | "event": "Audit", 12 | "integrations": {}, 13 | "messageId": "api-1U4n26Xe9IxnDgb2exPSXofpC9M", 14 | "originalTimestamp": "2019-11-24T19:42:03.977Z", 15 | "properties": { 16 | "details": { 17 | "resource_id": "spaces/default/warehouse-sources/whs_1U34CpJKr1BiqO933sY0y2Y4wT7", 18 | "resource_type": "personas_warehouse_source", 19 | "subject": "users/MNxxtIRCbe", 20 | "target": "Postgres Warehouse" 21 | }, 22 | "type": "Personas Warehouse Source Modified", 23 | "workspace_id": "VWDxzzIpI5" 24 | }, 25 | "receivedAt": "2019-11-24T19:42:04.145Z", 26 | "replay": true, 27 | "timestamp": "2019-11-24T19:42:03.977Z", 28 | "type": "track", 29 | "userId": "MNxxtIRCbe" 30 | }, 31 | "headers": { 32 | "Accept-Encoding": [ 33 | "gzip, deflate", 34 | "identity" 35 | ], 36 | "Content-Length": [ 37 | "621" 38 | ], 39 | "Content-Type": [ 40 | "application/json" 41 | ], 42 | "Date": [ 43 | "Sun, 24 Nov 2019 19:42:41 GMT" 44 | ], 45 | "User-Agent": [ 46 | "Segment.io/1.0" 47 | ], 48 | "X-Request-Id": [ 49 | "f059c065-f166-41bb-9395-415f781d1f5e" 50 | ] 51 | }, 52 | "queryParameters": {} 53 | } 54 | } -------------------------------------------------------------------------------- /sources/audit-forwarding/webhook-examples/schema*.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "channel": "server", 5 | "context": { 6 | "library": { 7 | "name": "unknown", 8 | "version": "unknown" 9 | } 10 | }, 11 | "event": "Audit", 12 | "integrations": {}, 13 | "messageId": "api-1U4pLpDiuoddGHK2GE5ZvAl112v", 14 | "originalTimestamp": "2019-11-24T20:01:07.945Z", 15 | "properties": { 16 | "details": { 17 | "description": "`Order Completed` set to block", 18 | "resource_id": "3LcUdkMjau", 19 | "resource_type": "source", 20 | "source_id": "3LcUdkMjau", 21 | "subject": "users/MNxxtIRCbe", 22 | "target": "JavaScript" 23 | }, 24 | "type": "Schema Event Blocked", 25 | "workspace_id": "VWDxzzIpI5" 26 | }, 27 | "receivedAt": "2019-11-24T20:01:07.968Z", 28 | "replay": true, 29 | "timestamp": "2019-11-24T20:01:07.945Z", 30 | "type": "track", 31 | "userId": "MNxxtIRCbe" 32 | }, 33 | "headers": { 34 | "Accept-Encoding": [ 35 | "gzip, deflate", 36 | "identity" 37 | ], 38 | "Content-Length": [ 39 | "598" 40 | ], 41 | "Content-Type": [ 42 | "application/json" 43 | ], 44 | "Date": [ 45 | "Sun, 24 Nov 2019 20:01:34 GMT" 46 | ], 47 | "User-Agent": [ 48 | "Segment.io/1.0" 49 | ], 50 | "X-Request-Id": [ 51 | "425325df-2b46-4af0-829b-15f3944a3bb9" 52 | ] 53 | }, 54 | "queryParameters": {} 55 | } 56 | } -------------------------------------------------------------------------------- /sources/audit-forwarding/webhook-examples/source*.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "channel": "server", 5 | "context": { 6 | "library": { 7 | "name": "unknown", 8 | "version": "unknown" 9 | } 10 | }, 11 | "event": "Audit", 12 | "integrations": {}, 13 | "messageId": "api-1U3EOmw0K4vJ54hnsAOPAnuXMwi", 14 | "originalTimestamp": "2019-11-24T06:27:28Z", 15 | "properties": { 16 | "details": { 17 | "resource_id": "LAn9KpdvNZ", 18 | "resource_type": "source", 19 | "source_id": "LAn9KpdvNZ", 20 | "subject": "users/MNxxtIRCbe", 21 | "target": "Audit Function" 22 | }, 23 | "type": "Source Function Updated", 24 | "workspace_id": "VWDxzzIpI5" 25 | }, 26 | "receivedAt": "2019-11-24T06:27:28.064Z", 27 | "replay": true, 28 | "timestamp": "2019-11-24T06:27:28.000Z", 29 | "type": "track", 30 | "userId": "MNxxtIRCbe" 31 | }, 32 | "headers": { 33 | "Accept-Encoding": [ 34 | "gzip, deflate", 35 | "identity" 36 | ], 37 | "Content-Length": [ 38 | "554" 39 | ], 40 | "Content-Type": [ 41 | "application/json" 42 | ], 43 | "Date": [ 44 | "Sun, 24 Nov 2019 19:30:31 GMT" 45 | ], 46 | "User-Agent": [ 47 | "Segment.io/1.0" 48 | ], 49 | "X-Request-Id": [ 50 | "3d1f0bfc-6d5d-49ed-a1f5-5ee98a96b03c" 51 | ] 52 | }, 53 | "queryParameters": {} 54 | } 55 | } -------------------------------------------------------------------------------- /sources/audit-forwarding/webhook-examples/space*.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "channel": "server", 5 | "context": { 6 | "library": { 7 | "name": "unknown", 8 | "version": "unknown" 9 | } 10 | }, 11 | "event": "Audit", 12 | "integrations": {}, 13 | "messageId": "api-1U3DHZbAl3rqo36Yv7gs09Io1Cp", 14 | "originalTimestamp": "2019-11-24T06:18:17.91Z", 15 | "properties": { 16 | "details": { 17 | "resource_id": "spa_1U3CcdsPUbEhRudvj0HorhJodwJ", 18 | "resource_type": "space", 19 | "subject": "users/MNxxtIRCbe", 20 | "target": "test space" 21 | }, 22 | "type": "Space Modified", 23 | "workspace_id": "VWDxzzIpI5" 24 | }, 25 | "receivedAt": "2019-11-24T06:18:17.947Z", 26 | "replay": true, 27 | "timestamp": "2019-11-24T06:18:17.910Z", 28 | "type": "track", 29 | "userId": "MNxxtIRCbe" 30 | }, 31 | "headers": { 32 | "Accept-Encoding": [ 33 | "gzip, deflate", 34 | "identity" 35 | ], 36 | "Content-Length": [ 37 | "539" 38 | ], 39 | "Content-Type": [ 40 | "application/json" 41 | ], 42 | "Date": [ 43 | "Sun, 24 Nov 2019 19:21:42 GMT" 44 | ], 45 | "User-Agent": [ 46 | "Segment.io/1.0" 47 | ], 48 | "X-Request-Id": [ 49 | "0aceaebe-c8c3-4ad7-bb6f-ee871396e247" 50 | ] 51 | }, 52 | "queryParameters": {} 53 | } 54 | } -------------------------------------------------------------------------------- /sources/audit-forwarding/webhook-examples/trackingPlan*.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "channel": "server", 5 | "context": { 6 | "library": { 7 | "name": "unknown", 8 | "version": "unknown" 9 | } 10 | }, 11 | "event": "Audit", 12 | "integrations": {}, 13 | "messageId": "api-1U4ogZka3nPQPgIDQvuTXcvMGWT", 14 | "originalTimestamp": "2019-11-24T19:55:40Z", 15 | "properties": { 16 | "details": { 17 | "resource_id": "tp_1RZpUzTbQVqffEURXB8IyXCjhIi", 18 | "resource_type": "tracking_plan", 19 | "subject": "users/MNxxtIRCbe", 20 | "target": "test" 21 | }, 22 | "type": "Tracking Plan Modified", 23 | "workspace_id": "VWDxzzIpI5" 24 | }, 25 | "receivedAt": "2019-11-24T19:55:40.791Z", 26 | "replay": true, 27 | "timestamp": "2019-11-24T19:55:40.000Z", 28 | "type": "track", 29 | "userId": "__system__" 30 | }, 31 | "headers": { 32 | "Accept-Encoding": [ 33 | "gzip, deflate", 34 | "identity" 35 | ], 36 | "Content-Length": [ 37 | "545" 38 | ], 39 | "Content-Type": [ 40 | "application/json" 41 | ], 42 | "Date": [ 43 | "Sun, 24 Nov 2019 19:59:47 GMT" 44 | ], 45 | "User-Agent": [ 46 | "Segment.io/1.0" 47 | ], 48 | "X-Request-Id": [ 49 | "74293323-4c6a-4e7c-8583-c5456f354edc" 50 | ] 51 | }, 52 | "queryParameters": {} 53 | } 54 | } -------------------------------------------------------------------------------- /sources/audit-forwarding/webhook-examples/warehouse*.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "channel": "server", 5 | "context": { 6 | "library": { 7 | "name": "unknown", 8 | "version": "unknown" 9 | } 10 | }, 11 | "event": "Audit", 12 | "integrations": {}, 13 | "messageId": "api-1U4o6IzwuVU9GB3VF2p4lHBqw2D", 14 | "originalTimestamp": "2019-11-24T19:50:51.744Z", 15 | "properties": { 16 | "details": { 17 | "resource_id": "HpLcqdFbPX", 18 | "resource_type": "warehouse", 19 | "subject": "users/MNxxtIRCbe", 20 | "target": "postgres", 21 | "warehouse_id": "HpLcqdFbPX" 22 | }, 23 | "type": "Warehouse Disabled", 24 | "workspace_id": "VWDxzzIpI5" 25 | }, 26 | "receivedAt": "2019-11-24T19:50:51.765Z", 27 | "replay": true, 28 | "timestamp": "2019-11-24T19:50:51.744Z", 29 | "type": "track", 30 | "userId": "MNxxtIRCbe" 31 | }, 32 | "headers": { 33 | "Accept-Encoding": [ 34 | "gzip, deflate", 35 | "identity" 36 | ], 37 | "Content-Length": [ 38 | "553" 39 | ], 40 | "Content-Type": [ 41 | "application/json" 42 | ], 43 | "Date": [ 44 | "Sun, 24 Nov 2019 19:51:18 GMT" 45 | ], 46 | "User-Agent": [ 47 | "Segment.io/1.0" 48 | ], 49 | "X-Request-Id": [ 50 | "a6049f4e-f0ca-4c01-a6ce-20856cf4fb12" 51 | ] 52 | }, 53 | "queryParameters": {} 54 | } 55 | } -------------------------------------------------------------------------------- /sources/close-io/webhook-examples/activity_call_created.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "headers": { 4 | "Accept": ["*/*"], 5 | "Accept-Encoding": ["gzip, deflate"], 6 | "Close-Sig-Hash": ["41d74dd701885ea5389b54327c1ba959adb6843370bb529a92dc381a10bf7d06"], 7 | "Close-Sig-Timestamp": ["1561595716"], 8 | "Content-Length": ["2020"], 9 | "Content-Type": ["application/json"], 10 | "User-Agent": ["Close Webhooks 2.0"], 11 | "X-Amzn-Trace-Id": ["Root=1-5d140f44-67779ba45bb40870956749de"], 12 | "X-Forwarded-For": ["52.27.236.234"], 13 | "X-Forwarded-Port": ["443"], 14 | "X-Forwarded-Proto": ["https"], 15 | "X-Request-Id": ["97889cad-2875-4d30-bcd3-c956ce7b7ccd"] 16 | }, 17 | "body": { 18 | "subscription_id": "whsub_0b8wXcKhybsWNKnvdeGE3S", 19 | "event": { 20 | "organization_id": "orga_7q9QmANUTdBpkIjSNjZX7nzkPxolW4xfYifHmXYlcRa", 21 | "object_id": "acti_O8H1eRursFaNuMj8mzWQnv6XPVgeEXD3Bzb3s5QzIjN", 22 | "action": "created", 23 | "previous_data": {}, 24 | "lead_id": "lead_6jtLjE3zOL2lYxQzalvEF7BTg8awGAXKAVlj6bo0wui", 25 | "user_id": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB", 26 | "date_updated": "2019-06-27T00:35:16.414000", 27 | "date_created": "2019-06-27T00:35:16.414000", 28 | "id": "ev_5ggZkIMaH5NXz2MdG6rcLQ", 29 | "request_id": "req_1yvwhrltBWyKsstacZEKY6", 30 | "data": { 31 | "_type": "Call", 32 | "lead_id": "lead_6jtLjE3zOL2lYxQzalvEF7BTg8awGAXKAVlj6bo0wui", 33 | "local_phone": null, 34 | "call_method": "external", 35 | "updated_by_name": "", 36 | "recording_url": null, 37 | "remote_phone": "+13102322342", 38 | "has_recording": false, 39 | "status": "completed", 40 | "direction": "outbound", 41 | "organization_id": "orga_7q9QmANUTdBpkIjSNjZX7nzkPxolW4xfYifHmXYlcRa", 42 | "quality_info": "", 43 | "transferred_to": null, 44 | "voicemail_duration": 0, 45 | "created_by": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB", 46 | "users": [], 47 | "created_by_name": "", 48 | "disposition": "answered", 49 | "remote_country_iso": "US", 50 | "remote_phone_formatted": "+1 310-232-2342", 51 | "id": "acti_O8H1eRursFaNuMj8mzWQnv6XPVgeEXD3Bzb3s5QzIjN", 52 | "is_to_group_number": false, 53 | "duration": 0, 54 | "local_country_iso": "", 55 | "date_updated": "2019-06-27T00:35:16.412000+00:00", 56 | "contact_id": "cont_qZrs0uDvtDHljl6IojkXypuXkEAsrKib3kU2Fijqhf0", 57 | "dialer_id": null, 58 | "cost": null, 59 | "updated_by": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB", 60 | "source": "External", 61 | "transferred_from": null, 62 | "phone": "+13102322342", 63 | "user_id": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB", 64 | "note": "", 65 | "local_phone_formatted": null, 66 | "dialer_saved_search_id": null, 67 | "user_name": "", 68 | "date_created": "2019-06-27T00:35:16.412000+00:00", 69 | "voicemail_url": null 70 | }, 71 | "meta": { 72 | "request_method": "POST", 73 | "request_path": "/api/v1/activity/call/" 74 | }, 75 | "changed_fields": [], 76 | "object_type": "activity.call", 77 | "api_key_id": null 78 | } 79 | }, 80 | "queryParameters": {} 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /sources/close-io/webhook-examples/activity_call_updated.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "headers": { 4 | "Accept": ["*/*"], 5 | "Accept-Encoding": ["gzip, deflate"], 6 | "Close-Sig-Hash": ["da40195205b1eb206c9b79e3794f88dc4d99fac116e42ec7912e24155d45b4a1"], 7 | "Close-Sig-Timestamp": ["1561595725"], 8 | "Content-Length": ["2167"], 9 | "Content-Type": ["application/json"], 10 | "User-Agent": ["Close Webhooks 2.0"], 11 | "X-Amzn-Trace-Id": ["Root=1-5d140f4d-e3e5b38a9b6d64fe1a7740b7"], 12 | "X-Forwarded-For": ["54.71.177.103"], 13 | "X-Forwarded-Port": ["443"], 14 | "X-Forwarded-Proto": ["https"], 15 | "X-Request-Id": ["ed381910-24ad-4d4c-90f4-4f5692ee0c42"] 16 | }, 17 | "body": { 18 | "event": { 19 | "date_created": "2019-06-27T00:35:18.513000", 20 | "data": { 21 | "date_created": "2019-06-27T00:35:16.412000+00:00", 22 | "local_country_iso": "", 23 | "disposition": "answered", 24 | "updated_by": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB", 25 | "status": "completed", 26 | "created_by_name": "Francisco Alio", 27 | "transferred_to": null, 28 | "voicemail_duration": 0, 29 | "lead_id": "lead_6jtLjE3zOL2lYxQzalvEF7BTg8awGAXKAVlj6bo0wui", 30 | "source": "External", 31 | "duration": 0, 32 | "created_by": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB", 33 | "user_id": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB", 34 | "dialer_id": null, 35 | "remote_country_iso": "US", 36 | "transferred_from": null, 37 | "recording_url": null, 38 | "call_method": "external", 39 | "has_recording": false, 40 | "cost": null, 41 | "remote_phone": "+13102322342", 42 | "user_name": "Francisco Alio", 43 | "users": [], 44 | "id": "acti_O8H1eRursFaNuMj8mzWQnv6XPVgeEXD3Bzb3s5QzIjN", 45 | "remote_phone_formatted": "+1 310-232-2342", 46 | "updated_by_name": "Francisco Alio", 47 | "contact_id": "cont_qZrs0uDvtDHljl6IojkXypuXkEAsrKib3kU2Fijqhf0", 48 | "organization_id": "orga_7q9QmANUTdBpkIjSNjZX7nzkPxolW4xfYifHmXYlcRa", 49 | "note": "Called Rebecca.", 50 | "phone": "+13102322342", 51 | "local_phone_formatted": null, 52 | "dialer_saved_search_id": null, 53 | "direction": "outbound", 54 | "voicemail_url": null, 55 | "quality_info": "", 56 | "local_phone": null, 57 | "_type": "Call", 58 | "is_to_group_number": false, 59 | "date_updated": "2019-06-27T00:35:21.031000+00:00" 60 | }, 61 | "id": "ev_7ZkVnofmVso3HxNINXRa06", 62 | "lead_id": "lead_6jtLjE3zOL2lYxQzalvEF7BTg8awGAXKAVlj6bo0wui", 63 | "previous_data": { 64 | "note": "", 65 | "date_updated": "2019-06-27T00:35:16.412000+00:00" 66 | }, 67 | "object_type": "activity.call", 68 | "user_id": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB", 69 | "action": "updated", 70 | "api_key_id": null, 71 | "object_id": "acti_O8H1eRursFaNuMj8mzWQnv6XPVgeEXD3Bzb3s5QzIjN", 72 | "meta": { 73 | "request_method": "PUT", 74 | "request_path": "/api/v1/activity/call/acti_O8H1eRursFaNuMj8mzWQnv6XPVgeEXD3Bzb3s5QzIjN/" 75 | }, 76 | "organization_id": "orga_7q9QmANUTdBpkIjSNjZX7nzkPxolW4xfYifHmXYlcRa", 77 | "date_updated": "2019-06-27T00:35:21.034000", 78 | "changed_fields": ["date_updated", "note"], 79 | "request_id": "req_6hbHjnhElepkGS9H25cTLR" 80 | }, 81 | "subscription_id": "whsub_0b8wXcKhybsWNKnvdeGE3S" 82 | }, 83 | "queryParameters": {} 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /sources/close-io/webhook-examples/activity_contact_updated.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "headers": { 4 | "Accept": ["*/*"], 5 | "Accept-Encoding": ["gzip, deflate"], 6 | "Close-Sig-Hash": ["1e167cac240ef4fe8ee8b40d5a3b9016e480cfc5c9668dbacfe41a6b844736ea"], 7 | "Close-Sig-Timestamp": ["1561598074"], 8 | "Content-Length": ["1434"], 9 | "Content-Type": ["application/json"], 10 | "User-Agent": ["Close Webhooks 2.0"], 11 | "X-Amzn-Trace-Id": ["Root=1-5d14187a-6eeee85b7c3524b25a8fe0f3"], 12 | "X-Forwarded-For": ["52.27.236.234"], 13 | "X-Forwarded-Port": ["443"], 14 | "X-Forwarded-Proto": ["https"], 15 | "X-Request-Id": ["1ce756f0-6369-4872-a5cc-20e574e1af2e"] 16 | }, 17 | "body": { 18 | "event": { 19 | "data": { 20 | "urls": [], 21 | "updated_by": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB", 22 | "organization_id": "orga_7q9QmANUTdBpkIjSNjZX7nzkPxolW4xfYifHmXYlcRa", 23 | "title": "CEO", 24 | "phones": [{ 25 | "phone_formatted": "+1 310-232-2312", 26 | "type": "office", 27 | "phone": "+13102322312" 28 | }], 29 | "date_updated": "2019-06-27T01:14:26.301000+00:00", 30 | "date_created": "2019-06-27T01:14:09.182000+00:00", 31 | "lead_id": "lead_PcFM8kynPHJGA8ox1Pbi4EQfwaY1zkcejPihu1LbBCY", 32 | "name": "Estella Line", 33 | "created_by": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB", 34 | "id": "cont_XSji72fbzkDyZijXKDMOOf1iVA5r70yWx5rnJxLvp0x", 35 | "emails": [] 36 | }, 37 | "request_id": "req_5U2IDPeXP0llRdj3DuP91n", 38 | "organization_id": "orga_7q9QmANUTdBpkIjSNjZX7nzkPxolW4xfYifHmXYlcRa", 39 | "api_key_id": null, 40 | "object_type": "contact", 41 | "action": "updated", 42 | "object_id": "cont_XSji72fbzkDyZijXKDMOOf1iVA5r70yWx5rnJxLvp0x", 43 | "previous_data": { 44 | "date_updated": "2019-06-27T01:14:09.182000+00:00", 45 | "phones": [], 46 | "title": "" 47 | }, 48 | "date_updated": "2019-06-27T01:14:26.302000", 49 | "date_created": "2019-06-27T01:14:26.302000", 50 | "user_id": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB", 51 | "lead_id": "lead_PcFM8kynPHJGA8ox1Pbi4EQfwaY1zkcejPihu1LbBCY", 52 | "meta": { 53 | "request_path": "/api/v1/contact/cont_XSji72fbzkDyZijXKDMOOf1iVA5r70yWx5rnJxLvp0x/", 54 | "request_method": "PUT" 55 | }, 56 | "id": "ev_6ABSZuThyvFW9mqIq2jfsb", 57 | "changed_fields": ["date_updated", "phones", "title"] 58 | }, 59 | "subscription_id": "whsub_0b8wXcKhybsWNKnvdeGE3S" 60 | }, 61 | "queryParameters": {} 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /sources/close-io/webhook-examples/activity_note_created.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "headers": { 4 | "Accept": ["*/*"], 5 | "Accept-Encoding": ["gzip, deflate"], 6 | "Close-Sig-Hash": ["f5dd49a113ed6559ac9156db541a1401b6d1fa74b85fddbdac71bf8c0db891ef"], 7 | "Close-Sig-Timestamp": ["1561595728"], 8 | "Content-Length": ["1370"], 9 | "Content-Type": ["application/json"], 10 | "User-Agent": ["Close Webhooks 2.0"], 11 | "X-Amzn-Trace-Id": ["Root=1-5d140f50-8b8cb5900eb99930af0fbfbb"], 12 | "X-Forwarded-For": ["52.27.236.234"], 13 | "X-Forwarded-Port": ["443"], 14 | "X-Forwarded-Proto": ["https"], 15 | "X-Request-Id": ["962ebbc7-e280-4906-9d33-5d5cf8454c57"] 16 | }, 17 | "body": { 18 | "event": { 19 | "request_id": "req_0kPg49iEtGtT8YHHLSI0ev", 20 | "date_updated": "2019-06-27T00:35:28.258000", 21 | "lead_id": "lead_6jtLjE3zOL2lYxQzalvEF7BTg8awGAXKAVlj6bo0wui", 22 | "previous_data": {}, 23 | "object_type": "activity.note", 24 | "user_id": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB", 25 | "api_key_id": null, 26 | "id": "ev_7M7ojqnWwhw5kXrD0WfTBA", 27 | "data": { 28 | "users": [], 29 | "date_updated": "2019-06-27T00:35:28.256000+00:00", 30 | "lead_id": "lead_6jtLjE3zOL2lYxQzalvEF7BTg8awGAXKAVlj6bo0wui", 31 | "updated_by": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB", 32 | "note": "T", 33 | "user_id": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB", 34 | "_type": "Note", 35 | "id": "acti_5sZvqwIpK42gSJtPmVZjM3RpuBGiLdZRBpGCd1rzPm9", 36 | "updated_by_name": "Francisco Alio", 37 | "created_by": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB", 38 | "created_by_name": "Francisco Alio", 39 | "date_created": "2019-06-27T00:35:28.256000+00:00", 40 | "contact_id": null, 41 | "user_name": "Francisco Alio", 42 | "organization_id": "orga_7q9QmANUTdBpkIjSNjZX7nzkPxolW4xfYifHmXYlcRa" 43 | }, 44 | "object_id": "acti_5sZvqwIpK42gSJtPmVZjM3RpuBGiLdZRBpGCd1rzPm9", 45 | "date_created": "2019-06-27T00:35:28.258000", 46 | "meta": { 47 | "request_method": "POST", 48 | "request_path": "/api/v1/activity/note/" 49 | }, 50 | "action": "created", 51 | "organization_id": "orga_7q9QmANUTdBpkIjSNjZX7nzkPxolW4xfYifHmXYlcRa", 52 | "changed_fields": [] 53 | }, 54 | "subscription_id": "whsub_0b8wXcKhybsWNKnvdeGE3S" 55 | }, 56 | "queryParameters": {} 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /sources/close-io/webhook-examples/activity_note_updated.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "headers": { 4 | "Accept": ["*/*"], 5 | "Accept-Encoding": ["gzip, deflate"], 6 | "Close-Sig-Hash": ["e398bb2474f8cd1daa4ac9019f46586f82feed7a25cc6906e75332897f5eb85b"], 7 | "Close-Sig-Timestamp": ["1561595737"], 8 | "Content-Length": ["1518"], 9 | "Content-Type": ["application/json"], 10 | "User-Agent": ["Close Webhooks 2.0"], 11 | "X-Amzn-Trace-Id": ["Root=1-5d140f59-2085b500a208d760d3adfbe8"], 12 | "X-Forwarded-For": ["54.71.177.103"], 13 | "X-Forwarded-Port": ["443"], 14 | "X-Forwarded-Proto": ["https"], 15 | "X-Request-Id": ["a020615e-8074-4185-aa67-153ac6a407a4"] 16 | }, 17 | "body": { 18 | "event": { 19 | "api_key_id": null, 20 | "object_type": "activity.note", 21 | "user_id": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB", 22 | "date_updated": "2019-06-27T00:35:30.667000", 23 | "request_id": "req_2vwr9Oqnn6ZWWw0SsczJzU", 24 | "changed_fields": ["date_updated", "note"], 25 | "lead_id": "lead_6jtLjE3zOL2lYxQzalvEF7BTg8awGAXKAVlj6bo0wui", 26 | "id": "ev_54sUUMkzNtjXT3muDNCmCq", 27 | "organization_id": "orga_7q9QmANUTdBpkIjSNjZX7nzkPxolW4xfYifHmXYlcRa", 28 | "object_id": "acti_5sZvqwIpK42gSJtPmVZjM3RpuBGiLdZRBpGCd1rzPm9", 29 | "previous_data": { 30 | "date_updated": "2019-06-27T00:35:28.256000+00:00", 31 | "note": "T" 32 | }, 33 | "action": "updated", 34 | "meta": { 35 | "request_method": "PUT", 36 | "request_path": "/api/v1/activity/note/acti_5sZvqwIpK42gSJtPmVZjM3RpuBGiLdZRBpGCd1rzPm9/" 37 | }, 38 | "data": { 39 | "_type": "Note", 40 | "contact_id": null, 41 | "note": "This is so cool!", 42 | "created_by": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB", 43 | "date_updated": "2019-06-27T00:35:30.669000+00:00", 44 | "lead_id": "lead_6jtLjE3zOL2lYxQzalvEF7BTg8awGAXKAVlj6bo0wui", 45 | "created_by_name": "Francisco Alio", 46 | "user_id": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB", 47 | "organization_id": "orga_7q9QmANUTdBpkIjSNjZX7nzkPxolW4xfYifHmXYlcRa", 48 | "updated_by": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB", 49 | "user_name": "Francisco Alio", 50 | "id": "acti_5sZvqwIpK42gSJtPmVZjM3RpuBGiLdZRBpGCd1rzPm9", 51 | "users": [], 52 | "date_created": "2019-06-27T00:35:28.256000+00:00", 53 | "updated_by_name": "Francisco Alio" 54 | }, 55 | "date_created": "2019-06-27T00:35:29.406000" 56 | }, 57 | "subscription_id": "whsub_0b8wXcKhybsWNKnvdeGE3S" 58 | }, 59 | "queryParameters": {} 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /sources/close-io/webhook-examples/contact_created.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "headers": { 4 | "Accept": ["*/*"], 5 | "Accept-Encoding": ["gzip, deflate"], 6 | "Close-Sig-Hash": ["23a2ec875d72e3fff4d3db90e9664f3f2d596aaa133846fa3541a6c4ef492a5a"], 7 | "Close-Sig-Timestamp": ["1561598049"], 8 | "Content-Length": ["1189"], 9 | "Content-Type": ["application/json"], 10 | "User-Agent": ["Close Webhooks 2.0"], 11 | "X-Amzn-Trace-Id": ["Root=1-5d141861-e7e64df87e7a0268e49f4838"], 12 | "X-Forwarded-For": ["52.27.236.234"], 13 | "X-Forwarded-Port": ["443"], 14 | "X-Forwarded-Proto": ["https"], 15 | "X-Request-Id": ["5dcb0608-b109-47e3-bb28-42b5fcb56bb1"] 16 | }, 17 | "body": { 18 | "event": { 19 | "changed_fields": [], 20 | "lead_id": "lead_PcFM8kynPHJGA8ox1Pbi4EQfwaY1zkcejPihu1LbBCY", 21 | "organization_id": "orga_7q9QmANUTdBpkIjSNjZX7nzkPxolW4xfYifHmXYlcRa", 22 | "user_id": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB", 23 | "action": "created", 24 | "meta": { 25 | "request_path": "/api/v1/lead/", 26 | "request_method": "POST" 27 | }, 28 | "id": "ev_6kDjN8cy4aidfHJD77zYKO", 29 | "date_created": "2019-06-27T01:14:09.185000", 30 | "date_updated": "2019-06-27T01:14:09.185000", 31 | "data": { 32 | "emails": [], 33 | "organization_id": "orga_7q9QmANUTdBpkIjSNjZX7nzkPxolW4xfYifHmXYlcRa", 34 | "updated_by": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB", 35 | "phones": [], 36 | "urls": [], 37 | "lead_id": "lead_PcFM8kynPHJGA8ox1Pbi4EQfwaY1zkcejPihu1LbBCY", 38 | "name": "Estella Line", 39 | "date_created": "2019-06-27T01:14:09.182000+00:00", 40 | "date_updated": "2019-06-27T01:14:09.182000+00:00", 41 | "created_by": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB", 42 | "id": "cont_XSji72fbzkDyZijXKDMOOf1iVA5r70yWx5rnJxLvp0x", 43 | "title": "" 44 | }, 45 | "object_id": "cont_XSji72fbzkDyZijXKDMOOf1iVA5r70yWx5rnJxLvp0x", 46 | "object_type": "contact", 47 | "api_key_id": null, 48 | "request_id": "req_4Kut7rkgoAigDM3FWbTGUQ", 49 | "previous_data": {} 50 | }, 51 | "subscription_id": "whsub_0b8wXcKhybsWNKnvdeGE3S" 52 | }, 53 | "queryParameters": {} 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /sources/close-io/webhook-examples/contact_updated.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "headers": { 4 | "Accept": ["*/*"], 5 | "Accept-Encoding": ["gzip, deflate"], 6 | "Close-Sig-Hash": ["a562e531fcf820fc7c68ee7f36d8ff172d4cb55872d962433cafa678210099a6"], 7 | "Close-Sig-Timestamp": ["1561595659"], 8 | "Content-Length": ["1381"], 9 | "Content-Type": ["application/json"], 10 | "User-Agent": ["Close Webhooks 2.0"], 11 | "X-Amzn-Trace-Id": ["Root=1-5d140f0b-ade5d798fd2992387b6cdc20"], 12 | "X-Forwarded-For": ["52.32.48.248"], 13 | "X-Forwarded-Port": ["443"], 14 | "X-Forwarded-Proto": ["https"], 15 | "X-Request-Id": ["ed71545b-9f6a-496d-86f7-19ec3e9cf956"] 16 | }, 17 | "body": { 18 | "subscription_id": "whsub_0b8wXcKhybsWNKnvdeGE3S", 19 | "event": { 20 | "object_type": "contact", 21 | "data": { 22 | "name": "Rebecca Guru", 23 | "urls": [], 24 | "emails": [{ 25 | "email": "rebecca@example.com", 26 | "type": "office" 27 | }], 28 | "date_updated": "2019-06-27T00:34:10.945000+00:00", 29 | "date_created": "2019-06-27T00:15:55.447000+00:00", 30 | "created_by": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB", 31 | "phones": [], 32 | "id": "cont_qZrs0uDvtDHljl6IojkXypuXkEAsrKib3kU2Fijqhf0", 33 | "updated_by": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB", 34 | "title": "CEO", 35 | "organization_id": "orga_7q9QmANUTdBpkIjSNjZX7nzkPxolW4xfYifHmXYlcRa", 36 | "lead_id": "lead_6jtLjE3zOL2lYxQzalvEF7BTg8awGAXKAVlj6bo0wui" 37 | }, 38 | "changed_fields": ["date_updated", "emails"], 39 | "action": "updated", 40 | "previous_data": { 41 | "emails": [], 42 | "date_updated": "2019-06-27T00:16:07.187000+00:00" 43 | }, 44 | "date_updated": "2019-06-27T00:34:10.944000", 45 | "date_created": "2019-06-27T00:34:10.944000", 46 | "id": "ev_2dzvsEyLQIhPn1gjctwhHd", 47 | "meta": { 48 | "request_path": "/api/v1/contact/cont_qZrs0uDvtDHljl6IojkXypuXkEAsrKib3kU2Fijqhf0/", 49 | "request_method": "PUT" 50 | }, 51 | "request_id": "req_78mbh2sL9TpngDL2wN4oXe", 52 | "organization_id": "orga_7q9QmANUTdBpkIjSNjZX7nzkPxolW4xfYifHmXYlcRa", 53 | "user_id": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB", 54 | "lead_id": "lead_6jtLjE3zOL2lYxQzalvEF7BTg8awGAXKAVlj6bo0wui", 55 | "api_key_id": null, 56 | "object_id": "cont_qZrs0uDvtDHljl6IojkXypuXkEAsrKib3kU2Fijqhf0" 57 | } 58 | }, 59 | "queryParameters": {} 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /sources/close-io/webhook-examples/lead_created.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "headers": { 4 | "Accept": ["*/*"], 5 | "Accept-Encoding": ["gzip, deflate"], 6 | "Close-Sig-Hash": ["e92ccf444a28a321e010e77c5540c4f41dab5646a4f392dba5db85fd4f6ddbed"], 7 | "Close-Sig-Timestamp": ["1561598050"], 8 | "Content-Length": ["1390"], 9 | "Content-Type": ["application/json"], 10 | "User-Agent": ["Close Webhooks 2.0"], 11 | "X-Amzn-Trace-Id": ["Root=1-5d141862-2f00dc34d74d8fbe5d1513ba"], 12 | "X-Forwarded-For": ["52.27.236.234"], 13 | "X-Forwarded-Port": ["443"], 14 | "X-Forwarded-Proto": ["https"], 15 | "X-Request-Id": ["b4d7e6aa-f4da-453b-bde8-04a4f02c78a6"] 16 | }, 17 | "body": { 18 | "event": { 19 | "object_id": "lead_PcFM8kynPHJGA8ox1Pbi4EQfwaY1zkcejPihu1LbBCY", 20 | "date_created": "2019-06-27T01:14:09.196000", 21 | "action": "created", 22 | "id": "ev_7Jn3AUNpILHqsta4DrHm5E", 23 | "request_id": "req_4Kut7rkgoAigDM3FWbTGUQ", 24 | "meta": { 25 | "request_path": "/api/v1/lead/", 26 | "request_method": "POST" 27 | }, 28 | "user_id": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB", 29 | "changed_fields": [], 30 | "previous_data": {}, 31 | "organization_id": "orga_7q9QmANUTdBpkIjSNjZX7nzkPxolW4xfYifHmXYlcRa", 32 | "data": { 33 | "url": null, 34 | "addresses": [], 35 | "id": "lead_PcFM8kynPHJGA8ox1Pbi4EQfwaY1zkcejPihu1LbBCY", 36 | "updated_by": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB", 37 | "status_label": "Potential", 38 | "created_by_name": "Francisco Alio", 39 | "organization_id": "orga_7q9QmANUTdBpkIjSNjZX7nzkPxolW4xfYifHmXYlcRa", 40 | "created_by": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB", 41 | "date_updated": "2019-06-27T01:14:09.195000+00:00", 42 | "date_created": "2019-06-27T01:14:09.167000+00:00", 43 | "name": "Fullstory", 44 | "contact_ids": ["cont_XSji72fbzkDyZijXKDMOOf1iVA5r70yWx5rnJxLvp0x"], 45 | "status_id": "stat_5LnqmxDs7YxsiHbzUJk6oXCfEg0oCisqDQKJXnyiB61", 46 | "display_name": "Fullstory", 47 | "description": "", 48 | "updated_by_name": "Francisco Alio" 49 | }, 50 | "date_updated": "2019-06-27T01:14:09.196000", 51 | "lead_id": "lead_PcFM8kynPHJGA8ox1Pbi4EQfwaY1zkcejPihu1LbBCY", 52 | "object_type": "lead", 53 | "api_key_id": null 54 | }, 55 | "subscription_id": "whsub_0b8wXcKhybsWNKnvdeGE3S" 56 | }, 57 | "queryParameters": {} 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /sources/close-io/webhook-examples/lead_updated.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "headers": { 4 | "Accept": ["*/*"], 5 | "Accept-Encoding": ["gzip, deflate"], 6 | "Close-Sig-Hash": ["914bcb9ac862d82b7049e2746e1636adb8495c5816dbed4df2d8ab6eed09afd8"], 7 | "Close-Sig-Timestamp": ["1561598160"], 8 | "Content-Length": ["1713"], 9 | "Content-Type": ["application/json"], 10 | "User-Agent": ["Close Webhooks 2.0"], 11 | "X-Amzn-Trace-Id": ["Root=1-5d1418d0-b89317e016ae9d0659602899"], 12 | "X-Forwarded-For": ["52.27.236.234"], 13 | "X-Forwarded-Port": ["443"], 14 | "X-Forwarded-Proto": ["https"], 15 | "X-Request-Id": ["cbed61d5-5d82-49bc-9f7b-f214923fe411"] 16 | }, 17 | "body": { 18 | "subscription_id": "whsub_0b8wXcKhybsWNKnvdeGE3S", 19 | "event": { 20 | "id": "ev_1fwGDALEzx3pLbtq60ZAAj", 21 | "lead_id": "lead_PcFM8kynPHJGA8ox1Pbi4EQfwaY1zkcejPihu1LbBCY", 22 | "date_updated": "2019-06-27T01:15:52.435000", 23 | "date_created": "2019-06-27T01:15:52.435000", 24 | "action": "updated", 25 | "request_id": "req_612ejPAvj5AxuDU3HCyMmD", 26 | "previous_data": { 27 | "addresses": [], 28 | "date_updated": "2019-06-27T01:15:36.012000+00:00" 29 | }, 30 | "object_type": "lead", 31 | "data": { 32 | "status_id": "stat_5LnqmxDs7YxsiHbzUJk6oXCfEg0oCisqDQKJXnyiB61", 33 | "display_name": "Fullstory", 34 | "contact_ids": ["cont_XSji72fbzkDyZijXKDMOOf1iVA5r70yWx5rnJxLvp0x"], 35 | "created_by_name": "Francisco Alio", 36 | "status_label": "Potential", 37 | "addresses": [{ 38 | "city": "San Francisco", 39 | "country": "US", 40 | "label": "business", 41 | "address_2": "", 42 | "zipcode": "94131", 43 | "address_1": "332 California", 44 | "state": "CA" 45 | }], 46 | "updated_by_name": "Francisco Alio", 47 | "description": "The best company", 48 | "id": "lead_PcFM8kynPHJGA8ox1Pbi4EQfwaY1zkcejPihu1LbBCY", 49 | "date_updated": "2019-06-27T01:15:52.436000+00:00", 50 | "date_created": "2019-06-27T01:14:09.167000+00:00", 51 | "url": "https://fullstory.com", 52 | "updated_by": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB", 53 | "organization_id": "orga_7q9QmANUTdBpkIjSNjZX7nzkPxolW4xfYifHmXYlcRa", 54 | "name": "Fullstory", 55 | "created_by": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB" 56 | }, 57 | "object_id": "lead_PcFM8kynPHJGA8ox1Pbi4EQfwaY1zkcejPihu1LbBCY", 58 | "meta": { 59 | "request_path": "/api/v1/lead/lead_PcFM8kynPHJGA8ox1Pbi4EQfwaY1zkcejPihu1LbBCY/", 60 | "request_method": "PUT" 61 | }, 62 | "api_key_id": null, 63 | "changed_fields": ["addresses", "date_updated"], 64 | "organization_id": "orga_7q9QmANUTdBpkIjSNjZX7nzkPxolW4xfYifHmXYlcRa", 65 | "user_id": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB" 66 | } 67 | }, 68 | "queryParameters": {} 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /sources/close-io/webhook-examples/opportunity_created.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "headers": { 4 | "Accept": ["*/*"], 5 | "Accept-Encoding": ["gzip, deflate"], 6 | "Close-Sig-Hash": ["6992cb8c2e335a7bf5a22c6f994c2b025ed2feab8a3d5b2f4e99f58ab05ba2cd"], 7 | "Close-Sig-Timestamp": ["1561595618"], 8 | "Content-Length": ["1771"], 9 | "Content-Type": ["application/json"], 10 | "User-Agent": ["Close Webhooks 2.0"], 11 | "X-Amzn-Trace-Id": ["Root=1-5d140ee2-79e979b11c6be7f9305a121a"], 12 | "X-Forwarded-For": ["52.27.236.234"], 13 | "X-Forwarded-Port": ["443"], 14 | "X-Forwarded-Proto": ["https"], 15 | "X-Request-Id": ["44fd0469-c32c-4ae4-ad15-7111149a9aa8"] 16 | }, 17 | "body": { 18 | "subscription_id": "whsub_0b8wXcKhybsWNKnvdeGE3S", 19 | "event": { 20 | "api_key_id": null, 21 | "action": "created", 22 | "changed_fields": [], 23 | "previous_data": {}, 24 | "request_id": "req_1zXh8rI7PKPUN25ftvUCzX", 25 | "lead_id": "lead_6jtLjE3zOL2lYxQzalvEF7BTg8awGAXKAVlj6bo0wui", 26 | "user_id": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB", 27 | "organization_id": "orga_7q9QmANUTdBpkIjSNjZX7nzkPxolW4xfYifHmXYlcRa", 28 | "date_updated": "2019-06-27T00:33:37.844000", 29 | "object_type": "opportunity", 30 | "meta": { 31 | "request_method": "POST", 32 | "request_path": "/api/v1/opportunity/" 33 | }, 34 | "data": { 35 | "contact_name": "Rebecca Guru", 36 | "lead_name": "Customer.io", 37 | "created_by_name": "Francisco Alio", 38 | "value_period": "one_time", 39 | "status_label": "Active", 40 | "status_id": "stat_YbsIAG0jpChcg4jrouu45Ilhdf5U5WaV5wBnxmvx5qN", 41 | "value_currency": "USD", 42 | "confidence": 70, 43 | "date_updated": "2019-06-27T00:33:37.842000+00:00", 44 | "lead_id": "lead_6jtLjE3zOL2lYxQzalvEF7BTg8awGAXKAVlj6bo0wui", 45 | "value_formatted": "$34,345", 46 | "value": 3434500, 47 | "date_created": "2019-06-27T00:33:37.842000+00:00", 48 | "date_won": "2019-06-29", 49 | "user_name": "Francisco Alio", 50 | "updated_by": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB", 51 | "note": "This is going to be huge.", 52 | "integration_links": [], 53 | "status_type": "active", 54 | "organization_id": "orga_7q9QmANUTdBpkIjSNjZX7nzkPxolW4xfYifHmXYlcRa", 55 | "contact_id": "cont_qZrs0uDvtDHljl6IojkXypuXkEAsrKib3kU2Fijqhf0", 56 | "updated_by_name": "Francisco Alio", 57 | "id": "oppo_MxvuXUHkjPwsAglBTUyoarTh4KeC3dC9Z1RLX7UCQv4", 58 | "date_lost": null, 59 | "user_id": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB", 60 | "created_by": "user_jkKBEsYynjwxnXKxidVoVbl07mIjCizf2gEQVn23kDB" 61 | }, 62 | "id": "ev_6OIclIHPL0drnvsVsxxstW", 63 | "object_id": "oppo_MxvuXUHkjPwsAglBTUyoarTh4KeC3dC9Z1RLX7UCQv4", 64 | "date_created": "2019-06-27T00:33:37.844000" 65 | } 66 | }, 67 | "queryParameters": {} 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /sources/default/handler.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Please do not delete [used for Intellisense] 3 | * @param {ServerRequest} request The incoming webhook request 4 | * @param {Object.} settings Custom settings 5 | * @return void 6 | */ 7 | async function onRequest(request, settings) { 8 | // get request body, header, and query string parameter 9 | const requestBody = request.json() 10 | const requestHeader = request.headers.get("X-Signature") 11 | const requestParam = request.url.searchParams.get("timestamp") 12 | 13 | // Emit messages of various types 14 | Segment.identify({ 15 | userId: "1234", 16 | traits: { 17 | traitName: "Example Trait" 18 | } 19 | }) 20 | 21 | Segment.track({ 22 | event: "Event Name", 23 | userId: "1234", 24 | properties: { 25 | propertyName: "Example Property" 26 | } 27 | }) 28 | 29 | Segment.group({ 30 | userId: "1234", 31 | groupId: "1234", 32 | traits: { 33 | traitName: "Example Trait" 34 | } 35 | }) 36 | 37 | Segment.set({ 38 | collection: "collection_name_plural", 39 | id: "object_id_string", 40 | properties: { 41 | propertyName: "Example Property" 42 | } 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /sources/default/webhook-examples/payload.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": {}, 4 | "headers": {}, 5 | "queryParameters": {} 6 | } 7 | } -------------------------------------------------------------------------------- /sources/formstack/handler.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Please do not delete [used for Intellisense] 3 | * @param {ServerRequest} request The incoming webhook request 4 | * @param {Object.} settings Custom settings 5 | * @return void 6 | */ 7 | async function onRequest(request, settings) { 8 | const msg = request.json() 9 | const { first, last } = msg.Name.value 10 | const { email } = msg.Email.value 11 | const userId = msg.UniqueID 12 | const formId = msg.FormID 13 | const optin = msg.optin.value ? true : false 14 | 15 | // create Leads in SFDC or user profiles downstream 16 | Segment.identify({ 17 | userId, 18 | traits: { 19 | firstName: first, 20 | lastName: last, 21 | email, 22 | optin 23 | } 24 | }) 25 | 26 | // send explicit track event for funnel analyses and audiencing downstream 27 | Segment.track({ 28 | event: 'Form Submitted', 29 | userId, 30 | properties: { formId, email } 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /sources/formstack/webhook-examples/form.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "FormID": "3557005", 5 | "UniqueID": "530120339", 6 | "Sign up for our newsletter!": { 7 | "value": "", 8 | "type": "section" 9 | }, 10 | "Name": { 11 | "value": { 12 | "first": "Garen", 13 | "last": "Damacia" 14 | }, 15 | "type": "name" 16 | }, 17 | "Email": { 18 | "value": "garen+test@segment.com", 19 | "type": "email" 20 | }, 21 | "optin": { 22 | "value": null, 23 | "type": "checkbox" 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /sources/github/handler.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Please do not delete [used for Intellisense] 3 | * @param {ServerRequest} request The incoming webhook request 4 | * @param {Object.} settings Custom settings 5 | * @return void 6 | */ 7 | async function onRequest(request, settings) { 8 | const body = request.json() 9 | var evt = request.headers.get("X-Github-Event") 10 | 11 | if (body.action) { 12 | evt += ` ${body.action}` 13 | } 14 | 15 | Segment.track({ 16 | event: evt, 17 | userId: `${body.sender.id}` 18 | }) 19 | } -------------------------------------------------------------------------------- /sources/google-sheets/handler.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Please do not delete [used for Intellisense] 3 | * @param {ServerRequest} request The incoming webhook request 4 | * @param {Object.} settings Custom settings 5 | * @return void 6 | */ 7 | async function onRequest(request, settings) { 8 | let eventBody = request.json(); 9 | 10 | Segment.identify({ 11 | userId: eventBody.userId, 12 | traits: { 13 | status: eventBody.status 14 | }, 15 | context: { 16 | source: "google sheets" 17 | } 18 | }) 19 | 20 | Segment.track({ 21 | event: "Row Updated", 22 | userId: eventBody.userId, 23 | properties: { 24 | email: eventBody.email, 25 | status: eventBody.status 26 | }, 27 | context: { 28 | source: "google sheets" 29 | } 30 | }) 31 | } -------------------------------------------------------------------------------- /sources/google-sheets/webhook-examples/row-change.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "headers": {}, 4 | "queryParams": {}, 5 | "body": { 6 | "userId": "use_123", 7 | "email": "example@example.com", 8 | "status": "churn" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /sources/influitive/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segmentio/functions-library/aa49c0ef635b5f030806c7fe1a771bbe9ccdac6f/sources/influitive/.DS_Store -------------------------------------------------------------------------------- /sources/influitive/handler.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Please do not delete [used for Intellisense] 3 | * @param {ServerRequest} request The incoming webhook request 4 | * @param {Object.} settings Custom settings 5 | * @return void 6 | */ 7 | async function onRequest(request, settings) { 8 | let event = request.json() 9 | 10 | if (event.name == 'Completed Challenge') { 11 | createChallengeCompletedEvent(event) 12 | } else if(event.name == 'Earned Badge') { 13 | createEarnedBadgeEvent(event) 14 | } else if(event.name == 'Joined AdvocateHub Group') { 15 | createJoinedGroupEvent(event) 16 | } else if (event.name == 'Advocate Joined') { 17 | createAdvocateJoinedEvent(event) 18 | } else { 19 | console.log("Unsupported Event: " + event.name); 20 | } 21 | } 22 | 23 | function createChallengeCompletedEvent(event) { 24 | Segment.track({ 25 | event: 'Challenge Completed', 26 | userId: event.contact.email, 27 | properties: { 28 | name: event.challenge.name, 29 | company: event.contact.company, 30 | points: event.points, 31 | challengeId: event.challenge.id, 32 | challengeType: event.challenge.challenge_type 33 | } 34 | }) 35 | } 36 | 37 | function createEarnedBadgeEvent(event) { 38 | Segment.track({ 39 | event: 'Badge Earned', 40 | userId: event.contact.email, 41 | properties: { 42 | name: event.parameters.name, 43 | description: event.parameters.description, 44 | points: event.points, 45 | sourceName: event.source_name, 46 | sourceType: event.source_type 47 | } 48 | }) 49 | } 50 | 51 | function createJoinedGroupEvent(event) { 52 | Segment.track({ 53 | event: 'Group Joined', 54 | userId: event.contact.email, 55 | properties: { 56 | group: event.parameters.group, 57 | groupId: event.parameters.group_id, 58 | groupType: event.parameters.type, 59 | points: event.points 60 | } 61 | }) 62 | } 63 | 64 | function createAdvocateJoinedEvent(event) { 65 | Segment.track({ 66 | event: 'Advocated Joined', 67 | userId: event.contact.email, 68 | properties: { 69 | type: event.type, 70 | sourceName: event.source_name, 71 | points: event.points, 72 | sourceType: event.source_type 73 | } 74 | }) 75 | } -------------------------------------------------------------------------------- /sources/influitive/webhook-examples/advocateJoined.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "id": 12345, 5 | "type": "advocate_joined", 6 | "name": "Advocate Joined", 7 | "created": 1561561048, 8 | "source_type": "Contact", 9 | "source_name": "Test Name", 10 | "billable": false, 11 | "parameters": {}, 12 | "points": 0, 13 | "challenge": {}, 14 | "contact": { 15 | "id": 1234, 16 | "uuid": "4c8c32d80-a215-4213-b289-b67de2c4253a", 17 | "first_name": "Test", 18 | "last_name": "Name", 19 | "full_name": "Test Name", 20 | "email": "test-name@test.com", 21 | "company": null, 22 | "title": null, 23 | "contact_points": 0, 24 | "salesforce_id": null, 25 | "crm_metadata": {} 26 | }, 27 | "additional_data": {} 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /sources/influitive/webhook-examples/challengeCompleted.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "id": 123456, 5 | "type": "completed_challenge", 6 | "name": "Completed Challenge", 7 | "created": 1561498416, 8 | "source_type": "Activity", 9 | "source_name": "Influitive Community Name", 10 | "billable": false, 11 | "parameters": { 12 | "challenge_code": null 13 | }, 14 | "points": 0, 15 | "challenge": { 16 | "id": "ded72939-3aa2-4a5f-aede-b68f3c309987", 17 | "name": "This is an example challenge Name", 18 | "challenge_type": "survey" 19 | }, 20 | "contact": { 21 | "id": 1019, 22 | "uuid": "c61ad4a7-6e5e-40ae-be8a-a2c3119bfe0a", 23 | "first_name": "Test", 24 | "last_name": "User", 25 | "full_name": "Test User", 26 | "email": "test.user@test.com", 27 | "company": null, 28 | "title": null, 29 | "contact_points": 25, 30 | "salesforce_id": "", 31 | "crm_metadata": {} 32 | }, 33 | "additional_data": {} 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /sources/influitive/webhook-examples/earnedBadge.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "additional_data": {}, 5 | "billable": false, 6 | "challenge": {}, 7 | "contact": { 8 | "company": null, 9 | "contact_points": 0, 10 | "email": "test@test.com", 11 | "first_name": "Test", 12 | "full_name": "Test Test", 13 | "id": 1234, 14 | "last_name": "Test", 15 | "title": null, 16 | "uuid": "12dewq3-756e-eewqr3-b013-e2fc8dd15462" 17 | }, 18 | "created": 1561648220, 19 | "id": 12345, 20 | "name": "Earned Badge", 21 | "parameters": { 22 | "badge": "Welcome!", 23 | "description": "This is a description of the badge" 24 | }, 25 | "points": 0, 26 | "source_name": "Welcome!", 27 | "source_type": "AwardStatus", 28 | "type": "earned_badge" 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /sources/influitive/webhook-examples/groupJoined.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "additional_data": {}, 5 | "billable": false, 6 | "challenge": {}, 7 | "contact": { 8 | "company": null, 9 | "contact_points": 0, 10 | "crm_metadata": {}, 11 | "email": "test@test.com", 12 | "first_name": "Test", 13 | "full_name": "Test Test", 14 | "id": 1234, 15 | "last_name": "Test", 16 | "title": null, 17 | "uuid": "d233be34-52d9-49b6-ac24-f7650ebb4771" 18 | }, 19 | "created": 1561650328, 20 | "id": 22745, 21 | "name": "Joined AdvocateHub Group", 22 | "parameters": { 23 | "group": "Intro Group", 24 | "group_id": 116, 25 | "type": "implicit_group_join" 26 | }, 27 | "points": 0, 28 | "source_name": "Intro Group", 29 | "source_type": "Group", 30 | "type": "influitive_group_join" 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /sources/leanplum/handler.js: -------------------------------------------------------------------------------- 1 | // Supported Objects: n/a 2 | // Supported Events: Email lifecycle events: https://docs.leanplum.com/docs/webhooks 3 | 4 | const isBlank = (str) => { 5 | return (!str || /^\s*$/.test(str)); 6 | }; 7 | 8 | const mapToSegment = (eventParams) => { 9 | try { 10 | eventParams.properties = {}; 11 | 12 | // LP sends Unix epoch time stamps in ms 13 | eventParams.timestamp = new Date(parseInt(eventParams.timestamp)).toISOString(); 14 | 15 | if(eventParams.abTestID) { 16 | eventParams.event = "AB Test"; 17 | eventParams.properties.abTestID = eventParams.abTestID; 18 | eventParams.properties.variantID = eventParams.variantID; 19 | } else { 20 | eventParams.event = eventParams.channel + " " + eventParams.event; 21 | } 22 | 23 | if(isBlank(eventParams.userId)) { 24 | eventParams.userId = null; 25 | } 26 | 27 | if(!isBlank(eventParams.device_id)) { 28 | eventParams.context = { device: { id: eventParams.device_id }}; 29 | } else { 30 | eventParams.context = {}; 31 | } 32 | 33 | if(!isBlank(eventParams.parameters)) { 34 | // LP sends URL encoded JSON for the event parameters 35 | let params = JSON.parse(decodeURIComponent(eventParams.parameters)); 36 | eventParams.properties = Object.assign(eventParams.properties, params); 37 | } 38 | 39 | return { type: "track", 40 | timestamp: eventParams.timestamp, 41 | event: eventParams.event, 42 | userId: eventParams.userId, 43 | context: eventParams.context, 44 | properties: eventParams.properties }; 45 | } catch (e) { 46 | console.log("ERROR - Could not map event properties: ", e.message); 47 | return null; 48 | } 49 | }; 50 | 51 | exports.processEvents = async (event) => { 52 | let eventBody = event.payload.body; 53 | let eventHeaders = event.payload.headers; 54 | let queryParameters = event.payload.queryParameters; 55 | 56 | console.log("Start Processing."); 57 | let returnEvent = mapToSegment(queryParameters); 58 | 59 | let returnValue = { 60 | events: [{ ...returnEvent}] 61 | }; 62 | 63 | console.log("End Processing:", returnValue); 64 | 65 | return(returnValue) 66 | }; 67 | -------------------------------------------------------------------------------- /sources/leanplum/webhook-examples/emailClicked.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "headers": {}, 4 | "body": {}, 5 | "queryParams": { 6 | "channel": "Email", 7 | "device_id": "", 8 | "event": "Click", 9 | "message_id": "6184400932175872", 10 | "parameters": "{\"index\":1,\"url\":\"https:\\/\\/segment.com\"}", 11 | "template_name": "", 12 | "time": "1557338008000", 13 | "user_id": "Igor+fg2@seg.com" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /sources/leanplum/webhook-examples/emailSent.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "headers": {}, 4 | "body": {}, 5 | "queryParams": { 6 | "channel": "Email", 7 | "device_id": "00284B13-ADAA-4FA0-AB01-1005B1B0802C", 8 | "event": "Send", 9 | "message_id": "6184400932175872", 10 | "parameters": "{}", 11 | "template_name": "", 12 | "time": "1556904645032", 13 | "user_id": "Igor+fg2@segment.com" 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /sources/paypal/handler.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Paypal Supported Objects: order, plan, checkout-order 3 | * Paypal Supported Events: Order Completed 4 | * 5 | * Please do not delete [used for Intellisense] 6 | * @param {ServerRequest} request The incoming webhook request 7 | * @param {Object.} settings Custom settings 8 | * @return void 9 | */ 10 | async function onRequest(request, settings) { 11 | let eventBody = request.json() 12 | 13 | if (eventBody.resource_type == 'order') { 14 | Segment.set({ 15 | collection: eventBody.resource_type, 16 | id: eventBody.resource.id, 17 | properties: { 18 | createTime: eventBody.resource.create_time, 19 | updateTime: eventBody.resource.update_time, 20 | orderStatus: eventBody.event_type, // converts event_type as the status of the order 21 | revenue: eventBody.resource.amount.total, 22 | currency: eventBody.resource.amount.currency, 23 | transactionFeeValue: eventBody.resource.transaction_fee.value, 24 | transactionFeeCurrency: eventBody.resource.transaction_fee.currency, 25 | parentPayment: eventBody.resource.parent_payment, 26 | isFinalCapture: eventBody.resource.is_final_capture, 27 | state: eventBody.resource.state, 28 | webhookId: eventBody.id, // id of the incoming webhook 29 | source: 'Paypal' 30 | } 31 | }) 32 | } 33 | 34 | if (eventBody.resource_type == 'plan') { 35 | Segment.set({ 36 | collection: eventBody.resource_type, 37 | id: eventBody.resource.id, 38 | properties: { 39 | createTime: eventBody.resource.create_time, 40 | updateTime: eventBody.resource.update_time, 41 | status: eventBody.resource.status, 42 | tenureType: eventBody.resource.tenure_type, 43 | sequence: eventBody.resource.sequence, 44 | tier_mode: eventBody.resource.volume, 45 | webhookId: eventBody.id, // id of the incoming webhook 46 | source: 'Paypal' 47 | } 48 | }) 49 | } 50 | 51 | if (eventBody.resource_type == 'checkout-order') { 52 | const props = { 53 | createTime: eventBody.resource.create_time, 54 | updateTime: eventBody.resource.update_time, 55 | revenue: eventBody.resource.gross_amount.value, 56 | currency: eventBody.resource.gross_amount.currency_code, 57 | status: eventBody.resource.status, 58 | webhookId: eventBody.id, // id of the incoming webhook 59 | source: 'Paypal' 60 | } 61 | 62 | Segment.set({ 63 | collection: eventBody.resource_type, 64 | id: eventBody.resource.id, 65 | properties: props 66 | }) 67 | 68 | Segment.track({ 69 | event: 'Order Completed', 70 | userId: eventBody.resource.payer.payer_id, 71 | properties: props 72 | }) 73 | } 74 | } -------------------------------------------------------------------------------- /sources/paypal/webhook-examples/billingPlanCompleted.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "id": "WH-2N1678257S892762B-8MC99539P4557624Y", 5 | "event_version": "1.0", 6 | "create_time": "2016-04-28T11:21:44Z", 7 | "resource_type": "plan", 8 | "event_type": "BILLING.PLAN.CREATED", 9 | "summary": "A billing plan was created", 10 | "resource": { 11 | "id": "P-7LT50814996943336LSNDODY", 12 | "merchant_preferences": { 13 | "setup_fee": { 14 | "currency": "USD", 15 | "value": "1" 16 | }, 17 | "return_url": "http://www.paypal.com", 18 | "cancel_url": "http://www.yahoo.com", 19 | "auto_bill_amount": "YES", 20 | "initial_fail_amount_action": "CONTINUE", 21 | "max_fail_attempts": "21" 22 | }, 23 | "update_time": "2016-04-28T11:21:31.151Z", 24 | "description": "test web new", 25 | "payment_definitions": [ 26 | { 27 | "name": "Payment Definition-1", 28 | "type": "REGULAR", 29 | "frequency": "Month", 30 | "frequency_interval": "1", 31 | "amount": { 32 | "currency": "USD", 33 | "value": "10" 34 | }, 35 | "cycles": "15", 36 | "charge_models": [ 37 | { 38 | "type": "SHIPPING", 39 | "amount": { 40 | "currency": "USD", 41 | "value": "1" 42 | }, 43 | "id": "CHM-29V063578B632154XLSNDODY" 44 | }, 45 | { 46 | "type": "TAX", 47 | "amount": { 48 | "currency": "USD", 49 | "value": "2" 50 | }, 51 | "id": "CHM-72N78855RJ303084YLSNDODY" 52 | } 53 | ], 54 | "id": "PD-28U66480VB469201XLSNDODY" 55 | }, 56 | { 57 | "name": "Payment Definition-2", 58 | "type": "TRIAL", 59 | "frequency": "Month", 60 | "frequency_interval": "1", 61 | "amount": { 62 | "currency": "USD", 63 | "value": "5" 64 | }, 65 | "cycles": "5", 66 | "charge_models": [ 67 | { 68 | "type": "SHIPPING", 69 | "amount": { 70 | "currency": "USD", 71 | "value": "1" 72 | }, 73 | "id": "CHM-4CT119433N5199501LSNDODY" 74 | }, 75 | { 76 | "type": "TAX", 77 | "amount": { 78 | "currency": "USD", 79 | "value": "1" 80 | }, 81 | "id": "CHM-38H015979N656741TLSNDODY" 82 | } 83 | ], 84 | "id": "PD-99B78670BE878604GLSNDODY" 85 | } 86 | ], 87 | "name": "Fast Speed Plan", 88 | "state": "CREATED", 89 | "create_time": "2016-04-28T11:21:31.151Z", 90 | "links": [ 91 | { 92 | "href": "https://api.paypal.com/v1/payments/billing-plans/P-7LT50814996943336LSNDODY", 93 | "rel": "self", 94 | "method": "GET" 95 | } 96 | ], 97 | "type": "FIXED" 98 | }, 99 | "links": [ 100 | { 101 | "href": "https://api.paypal.com/v1/notifications/webhooks-events/WH-2N1678257S892762B-8MC99539P4557624Y", 102 | "rel": "self", 103 | "method": "GET" 104 | }, 105 | { 106 | "href": "https://api.paypal.com/v1/notifications/webhooks-events/WH-2N1678257S892762B-8MC99539P4557624Y/resend", 107 | "rel": "resend", 108 | "method": "POST" 109 | } 110 | ] 111 | }, 112 | "headers": {}, 113 | "queryParams": {} 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /sources/paypal/webhook-examples/billingPlanCreated.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "id": "WH-2N1678257S892762B-8MC99539P4557624Y", 5 | "event_version": "1.0", 6 | "create_time": "2016-04-28T11:21:44Z", 7 | "resource_type": "plan", 8 | "event_type": "BILLING.PLAN.CREATED", 9 | "summary": "A billing plan was created", 10 | "resource": { 11 | "id": "P-7LT50814996943336LSNDODY", 12 | "merchant_preferences": { 13 | "setup_fee": { 14 | "currency": "USD", 15 | "value": "1" 16 | }, 17 | "return_url": "http://www.paypal.com", 18 | "cancel_url": "http://www.yahoo.com", 19 | "auto_bill_amount": "YES", 20 | "initial_fail_amount_action": "CONTINUE", 21 | "max_fail_attempts": "21" 22 | }, 23 | "update_time": "2016-04-28T11:21:31.151Z", 24 | "description": "test web new", 25 | "payment_definitions": [{ 26 | "name": "Payment Definition-1", 27 | "type": "REGULAR", 28 | "frequency": "Month", 29 | "frequency_interval": "1", 30 | "amount": { 31 | "currency": "USD", 32 | "value": "10" 33 | }, 34 | "cycles": "15", 35 | "charge_models": [{ 36 | "type": "SHIPPING", 37 | "amount": { 38 | "currency": "USD", 39 | "value": "1" 40 | }, 41 | "id": "CHM-29V063578B632154XLSNDODY" 42 | }, 43 | { 44 | "type": "TAX", 45 | "amount": { 46 | "currency": "USD", 47 | "value": "2" 48 | }, 49 | "id": "CHM-72N78855RJ303084YLSNDODY" 50 | } 51 | ], 52 | "id": "PD-28U66480VB469201XLSNDODY" 53 | }, 54 | { 55 | "name": "Payment Definition-2", 56 | "type": "TRIAL", 57 | "frequency": "Month", 58 | "frequency_interval": "1", 59 | "amount": { 60 | "currency": "USD", 61 | "value": "5" 62 | }, 63 | "cycles": "5", 64 | "charge_models": [{ 65 | "type": "SHIPPING", 66 | "amount": { 67 | "currency": "USD", 68 | "value": "1" 69 | }, 70 | "id": "CHM-4CT119433N5199501LSNDODY" 71 | }, 72 | { 73 | "type": "TAX", 74 | "amount": { 75 | "currency": "USD", 76 | "value": "1" 77 | }, 78 | "id": "CHM-38H015979N656741TLSNDODY" 79 | } 80 | ], 81 | "id": "PD-99B78670BE878604GLSNDODY" 82 | } 83 | ], 84 | "name": "Fast Speed Plan", 85 | "state": "CREATED", 86 | "create_time": "2016-04-28T11:21:31.151Z", 87 | "links": [{ 88 | "href": "https://api.paypal.com/v1/payments/billing-plans/P-7LT50814996943336LSNDODY", 89 | "rel": "self", 90 | "method": "GET" 91 | }], 92 | "type": "FIXED" 93 | }, 94 | "links": [{ 95 | "href": "https://api.paypal.com/v1/notifications/webhooks-events/WH-2N1678257S892762B-8MC99539P4557624Y", 96 | "rel": "self", 97 | "method": "GET" 98 | }, 99 | { 100 | "href": "https://api.paypal.com/v1/notifications/webhooks-events/WH-2N1678257S892762B-8MC99539P4557624Y/resend", 101 | "rel": "resend", 102 | "method": "POST" 103 | } 104 | ] 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /sources/paypal/webhook-examples/paymentCaptureCompleted.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "id": "WH-58D329510W468432D-8HN650336L201105X", 5 | "event_version": "1.0", 6 | "create_time": "2019-02-14T21:50:07.940Z", 7 | "resource_type": "capture", 8 | "resource_version": "2.0", 9 | "event_type": "PAYMENT.CAPTURE.COMPLETED", 10 | "summary": "Payment completed for $ 2.51 USD", 11 | "resource": { 12 | "id": "27M47624FP291604U", 13 | "amount": { 14 | "currency_code": "USD", 15 | "value": "2.51" 16 | }, 17 | "final_capture": true, 18 | "seller_protection": { 19 | "status": "ELIGIBLE", 20 | "dispute_categories": [ 21 | "ITEM_NOT_RECEIVED", 22 | "UNAUTHORIZED_TRANSACTION" 23 | ] 24 | }, 25 | "seller_receivable_breakdown": { 26 | "gross_amount": { 27 | "currency_code": "USD", 28 | "value": "2.51" 29 | }, 30 | "paypal_fee": { 31 | "currency_code": "USD", 32 | "value": "0.37" 33 | }, 34 | "net_amount": { 35 | "currency_code": "USD", 36 | "value": "2.14" 37 | } 38 | }, 39 | "status": "COMPLETED", 40 | "create_time": "2019-02-14T21:49:58Z", 41 | "update_time": "2019-02-14T21:49:58Z", 42 | "links": [ 43 | { 44 | "href": "https://api.paypal.com/v2/payments/captures/27M47624FP291604U", 45 | "rel": "self", 46 | "method": "GET" 47 | }, 48 | { 49 | "href": "https://api.paypal.com/v2/payments/captures/27M47624FP291604U/refund", 50 | "rel": "refund", 51 | "method": "POST" 52 | }, 53 | { 54 | "href": "https://api.paypal.com/v2/payments/authorizations/7W5147081L658180V", 55 | "rel": "up", 56 | "method": "GET" 57 | } 58 | ] 59 | }, 60 | "links": [ 61 | { 62 | "href": "https://api.paypal.com/v1/notifications/webhooks-events/WH-58D329510W468432D-8HN650336L201105X", 63 | "rel": "self", 64 | "method": "GET" 65 | }, 66 | { 67 | "href": "https://api.paypal.com/v1/notifications/webhooks-events/WH-58D329510W468432D-8HN650336L201105X/resend", 68 | "rel": "resend", 69 | "method": "POST" 70 | } 71 | ] 72 | }, 73 | "headers": {}, 74 | "queryParams": {} 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /sources/paypal/webhook-examples/paymentCaptureDenied.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "id": "WH-4SW78779LY2325805-07E03580SX1414828", 5 | "event_version": "1.0", 6 | "create_time": "2019-02-14T22:20:08.370Z", 7 | "resource_type": "capture", 8 | "resource_version": "2.0", 9 | "event_type": "PAYMENT.CAPTURE.DENIED", 10 | "summary": "A AUD 2.51 AUD capture payment was denied", 11 | "resource": { 12 | "id": "7NW873794T343360M", 13 | "amount": { 14 | "currency_code": "AUD", 15 | "value": "2.51" 16 | }, 17 | "final_capture": true, 18 | "seller_protection": { 19 | "status": "ELIGIBLE", 20 | "dispute_categories": [ 21 | "ITEM_NOT_RECEIVED", 22 | "UNAUTHORIZED_TRANSACTION" 23 | ] 24 | }, 25 | "seller_receivable_breakdown": { 26 | "gross_amount": { 27 | "currency_code": "AUD", 28 | "value": "2.51" 29 | }, 30 | "net_amount": { 31 | "currency_code": "AUD", 32 | "value": "2.51" 33 | } 34 | }, 35 | "status": "DECLINED", 36 | "create_time": "2019-02-14T22:18:14Z", 37 | "update_time": "2019-02-14T22:20:01Z", 38 | "links": [ 39 | { 40 | "href": "https://api.paypal.com/v2/payments/captures/7NW873794T343360M", 41 | "rel": "self", 42 | "method": "GET" 43 | }, 44 | { 45 | "href": "https://api.paypal.com/v2/payments/captures/7NW873794T343360M/refund", 46 | "rel": "refund", 47 | "method": "POST" 48 | }, 49 | { 50 | "href": "https://api.paypal.com/v2/payments/authorizations/2W543679LP5841156", 51 | "rel": "up", 52 | "method": "GET" 53 | } 54 | ] 55 | }, 56 | "links": [ 57 | { 58 | "href": "https://api.paypal.com/v1/notifications/webhooks-events/WH-4SW78779LY2325805-07E03580SX1414828", 59 | "rel": "self", 60 | "method": "GET" 61 | }, 62 | { 63 | "href": "https://api.paypal.com/v1/notifications/webhooks-events/WH-4SW78779LY2325805-07E03580SX1414828/resend", 64 | "rel": "resend", 65 | "method": "POST" 66 | } 67 | ] 68 | }, 69 | "headers": {}, 70 | "queryParams": {} 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /sources/paypal/webhook-examples/paymentOrderCreated.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "id": "WHPOC2017-9WY437509V4499423", 5 | "event_version": "1.0", 6 | "create_time": "2017-02-14T18:28:16Z", 7 | "resource_type": "order", 8 | "event_type": "PAYMENT.ORDER.CREATED", 9 | "summary": "A Payment order is created", 10 | "resource": { 11 | "id": "O-7K226695UU6131610", 12 | "create_time": "2017-02-14T18:28:16Z", 13 | "update_time": "2017-02-14T18:28:37Z", 14 | "amount": { 15 | "total": "4.54", 16 | "currency": "USD" 17 | }, 18 | "is_final_capture": true, 19 | "state": "completed", 20 | "reasonCode": "None", 21 | "parent_payment": "PAY-8U555528BH195130VLCRUVPA", 22 | "transaction_fee": { 23 | "value": "0.43", 24 | "currency": "USD" 25 | }, 26 | "links": [{ 27 | "href": "https://www.api.paypal.com/v1/payments/capture/9WY437509V4499423", 28 | "rel": "self", 29 | "method": "GET" 30 | }, 31 | { 32 | "href": "https://www.api.paypal.com/v1/payments/capture/9WY437509V4499423/refund", 33 | "rel": "refund", 34 | "method": "POST" 35 | }, 36 | { 37 | "href": "https://www.api.paypal.com/v1/payments/orders/O-7K226695UU6131610", 38 | "rel": "order", 39 | "method": "GET" 40 | }, 41 | { 42 | "href": "https://www.api.paypal.com/v1/payments/payment/PAY-8U555528BH195130VLCRUVPA", 43 | "rel": "parent_payment", 44 | "method": "GET" 45 | } 46 | ] 47 | }, 48 | "links": [{ 49 | "href": "https://api.paypal.com/v1/notifications/webhooks-events/WHPOC2017-9WY437509V4499423", 50 | "rel": "self", 51 | "method": "GET" 52 | }, 53 | { 54 | "href": "https://api.paypal.com/v1/notifications/webhooks-events/WHPOC2017-9WY437509V4499423/resend", 55 | "rel": "resend", 56 | "method": "POST" 57 | } 58 | ] 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /sources/pipedrive/handler.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Please do not delete [used for Intellisense] 3 | * @param {ServerRequest} request The incoming webhook request 4 | * @param {Object.} settings Custom settings 5 | * @return void 6 | */ 7 | async function onRequest(request, settings) { 8 | return transform(request.json()); 9 | } 10 | 11 | function transform(event) { 12 | let eventData = event; 13 | let eventObject = eventData.current; 14 | 15 | if (eventObject == null) { 16 | console.log("No Event: " + eventData.meta.object); 17 | return 18 | } 19 | 20 | if (eventData.meta.object == 'deal') { 21 | createDealObject(eventData); 22 | } else if (eventData.meta.object == 'organization') { 23 | createOrganizationObject(eventData); 24 | } else if (eventData.meta.object == 'note') { 25 | createNoteObject(eventData); 26 | } else if (eventData.meta.object == 'person') { 27 | createPersonObject(eventData); 28 | } else { 29 | console.log("Unsupported Event: " + eventData.meta.object); 30 | } 31 | 32 | // Send an event when a deal is added to 33 | // trigger a workflow downstream 34 | if (eventData.event == 'added.deal') { 35 | Segment.track({ 36 | event: 'Deal Added', 37 | userId: "" + eventObject.user_id, 38 | properties: { 39 | name: eventObject.title, 40 | value: eventObject.weighted_value, 41 | probability: eventObject.deal_probability, 42 | status: eventObject.status, 43 | currency: eventObject.currency 44 | } 45 | }) 46 | } 47 | } 48 | 49 | function createDealObject(eventData) { 50 | let currentData = eventData.current; 51 | 52 | Segment.set({ 53 | collection: eventData.meta.object + "s", 54 | id: "" + eventData.meta.id, 55 | properties: { 56 | active: currentData.active, 57 | timeAdded: currentData.add_time, 58 | timeClosed: currentData.close_time, 59 | creatorUserId: currentData.creator_user_id, 60 | currency: currentData.currency, 61 | deleted: currentData.deleted, 62 | formattedValue: currentData.formatted_value, 63 | ownerName: currentData.owner_name, 64 | organizationId: currentData.org_id, 65 | organizationName: currentData.org_name, 66 | personId: currentData.person_id, 67 | personName: currentData.person_name, 68 | pipelineId: currentData.pipeline_id, 69 | stageId: currentData.stageId, 70 | status: currentData.status, 71 | title: currentData.title, 72 | value: currentData.value, 73 | wonTime: currentData.won_time 74 | } 75 | }) 76 | } 77 | 78 | function createOrganizationObject(eventData) { 79 | let currentData = eventData.current; 80 | 81 | Segment.set({ 82 | collection: eventData.meta.object + "s", 83 | id: "" + eventData.meta.id, 84 | properties: { 85 | active: currentData.active_flag, 86 | timeAdded: currentData.add_time, 87 | email: currentData.cc_email, 88 | closedDealCount: currentData.closed_deals_count, 89 | categoryId: currentData.category_id, 90 | countryCode: currentData.country_code, 91 | lastActivityDate: currentData.last_activity_date, 92 | lostDealsCount: currentData.lost_deals_count, 93 | name: currentData.name, 94 | nextActivityDate: currentData.next_activity_date, 95 | nextActivityTime: currentData.next_activity_time, 96 | openDealsCount: currentData.open_deals_count, 97 | ownerId: currentData.owner_id, 98 | ownerName: currentData.owner_name, 99 | updatedTime: currentData.update_time, 100 | wonDealsCount: currentData.won_deals_count 101 | } 102 | }) 103 | } 104 | 105 | function createNoteObject(eventData) { 106 | let currentData = eventData.current; 107 | 108 | Segment.set({ 109 | collection: eventData.meta.object + "s", 110 | id: "" + eventData.meta.id, 111 | properties: { 112 | active: currentData.active_flag, 113 | timeAdded: currentData.add_time, 114 | content: currentData.content, 115 | dealTitle: currentData.deal.title, 116 | dealId: currentData.deal_id, 117 | organizationId: currentData.org_id, 118 | organization_name: currentData.organization.name, 119 | personId: currentData.person_id, 120 | personName: currentData.person.name 121 | } 122 | }) 123 | } 124 | 125 | function createPersonObject(eventData) { 126 | let currentData = eventData.current; 127 | 128 | Segment.set({ 129 | collection: eventData.meta.object + "s", 130 | id: "" + eventData.meta.id, 131 | properties: { 132 | active: currentData.active_flag, 133 | timeAdded: currentData.add_time, 134 | email: currentData.cc_email, 135 | firstName: currentData.first_name, 136 | lastName: currentData.last_name, 137 | lastActivityDate: currentData.last_activity_date, 138 | name: currentData.name, 139 | openDealsCount: currentData.open_deals_count, 140 | organizationId: currentData.org_id, 141 | organizationName: currentData.org_name, 142 | ownerId: currentData.owner_id, 143 | ownerName: currentData.owner_name, 144 | updatedTime: currentData.updated_time, 145 | wonDealsCount: currentData.won_deals_count 146 | } 147 | }) 148 | } -------------------------------------------------------------------------------- /sources/pipedrive/webhook-examples/dealAdded.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "current": { 5 | "active": true, 6 | "activities_count": 0, 7 | "add_time": "2019-06-26 14:29:52", 8 | "cc_email": "test-b3a96b+deal1@pipedrivemail.com", 9 | "close_time": null, 10 | "creator_user_id": 9542701, 11 | "currency": "USD", 12 | "deleted": false, 13 | "done_activities_count": 0, 14 | "email_messages_count": 0, 15 | "expected_close_date": "2019-06-28", 16 | "files_count": 0, 17 | "first_won_time": null, 18 | "followers_count": 0, 19 | "formatted_value": "$1,000,000", 20 | "formatted_weighted_value": "$1,000,000", 21 | "id": 1, 22 | "last_activity_date": null, 23 | "last_activity_id": null, 24 | "last_incoming_mail_time": null, 25 | "last_outgoing_mail_time": null, 26 | "lost_reason": null, 27 | "lost_time": null, 28 | "next_activity_date": null, 29 | "next_activity_duration": null, 30 | "next_activity_id": null, 31 | "next_activity_note": null, 32 | "next_activity_subject": null, 33 | "next_activity_time": null, 34 | "next_activity_type": null, 35 | "notes_count": 0, 36 | "org_hidden": false, 37 | "org_id": 1, 38 | "org_name": "Test", 39 | "owner_name": "Test User", 40 | "participants_count": 0, 41 | "person_hidden": false, 42 | "person_id": 1, 43 | "person_name": "Test", 44 | "pipeline_id": 1, 45 | "probability": null, 46 | "products_count": 0, 47 | "reference_activities_count": 0, 48 | "rotten_time": null, 49 | "stage_change_time": null, 50 | "stage_id": 2, 51 | "stage_order_nr": 2, 52 | "status": "open", 53 | "title": "Test deal", 54 | "undone_activities_count": 0, 55 | "update_time": "2019-06-26 14:29:52", 56 | "user_id": 9542701, 57 | "value": 1000000, 58 | "visible_to": "3", 59 | "weighted_value": 1000000, 60 | "weighted_value_currency": "USD", 61 | "won_time": null 62 | }, 63 | "event": "added.deal", 64 | "matches_filters": { 65 | "current": [] 66 | }, 67 | "meta": { 68 | "action": "added", 69 | "company_id": 6340191, 70 | "host": "test-b3a96b.pipedrive.com", 71 | "id": 1, 72 | "is_bulk_update": false, 73 | "matches_filters": { 74 | "current": [] 75 | }, 76 | "object": "deal", 77 | "permitted_user_ids": [ 78 | 9542701 79 | ], 80 | "pipedrive_service_name": false, 81 | "timestamp": 1561559392, 82 | "timestamp_micro": 1561559392560811, 83 | "trans_pending": false, 84 | "user_id": 9542701, 85 | "v": 1, 86 | "webhook_id": "73109" 87 | }, 88 | "previous": null, 89 | "retry": 0, 90 | "v": 1 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /sources/pipedrive/webhook-examples/dealDeleted.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "current": null, 5 | "event": "deleted.deal", 6 | "matches_filters": { 7 | "current": [] 8 | }, 9 | "meta": { 10 | "action": "deleted", 11 | "company_id": 6340191, 12 | "host": "test-b3a96b.pipedrive.com", 13 | "id": 1, 14 | "is_bulk_update": false, 15 | "matches_filters": { 16 | "current": [] 17 | }, 18 | "object": "deal", 19 | "permitted_user_ids": [ 20 | 9542701 21 | ], 22 | "pipedrive_service_name": false, 23 | "timestamp": 1561559429, 24 | "timestamp_micro": 1561559429117148, 25 | "trans_pending": false, 26 | "user_id": 9542701, 27 | "v": 1, 28 | "webhook_id": "73109" 29 | }, 30 | "previous": { 31 | "active": true, 32 | "activities_count": 0, 33 | "add_time": "2019-06-26 14:29:52", 34 | "cc_email": "test-b3a96b+deal1@pipedrivemail.com", 35 | "close_time": null, 36 | "creator_user_id": 9542701, 37 | "currency": "USD", 38 | "deleted": false, 39 | "done_activities_count": 0, 40 | "email_messages_count": 0, 41 | "expected_close_date": "2019-06-28", 42 | "files_count": 0, 43 | "first_won_time": null, 44 | "followers_count": 1, 45 | "formatted_value": "$1,000,000", 46 | "formatted_weighted_value": "$1,000,000", 47 | "id": 1, 48 | "last_activity_date": null, 49 | "last_activity_id": null, 50 | "last_incoming_mail_time": null, 51 | "last_outgoing_mail_time": null, 52 | "lost_reason": null, 53 | "lost_time": null, 54 | "next_activity_date": null, 55 | "next_activity_duration": null, 56 | "next_activity_id": null, 57 | "next_activity_note": null, 58 | "next_activity_subject": null, 59 | "next_activity_time": null, 60 | "next_activity_type": null, 61 | "notes_count": 0, 62 | "org_hidden": false, 63 | "org_id": 1, 64 | "org_name": "Test", 65 | "owner_name": "Test User", 66 | "participants_count": 1, 67 | "person_hidden": false, 68 | "person_id": 1, 69 | "person_name": "Test", 70 | "pipeline_id": 1, 71 | "probability": null, 72 | "products_count": 0, 73 | "reference_activities_count": 0, 74 | "rotten_time": null, 75 | "stage_change_time": null, 76 | "stage_id": 2, 77 | "stage_order_nr": 2, 78 | "status": "open", 79 | "title": "Test deal", 80 | "undone_activities_count": 0, 81 | "update_time": "2019-06-26 14:29:52", 82 | "user_id": 9542701, 83 | "value": 1000000, 84 | "visible_to": "3", 85 | "weighted_value": 1000000, 86 | "weighted_value_currency": "USD", 87 | "won_time": null 88 | }, 89 | "retry": 0, 90 | "v": 1 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /sources/pipedrive/webhook-examples/noteAdded.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "current": { 5 | "active_flag": true, 6 | "add_time": "2019-06-26 14:30:52", 7 | "content": "Testing out the notes functionality", 8 | "deal": { 9 | "title": "Test deal" 10 | }, 11 | "deal_id": 1, 12 | "id": 1, 13 | "last_update_user_id": null, 14 | "org_id": 1, 15 | "organization": { 16 | "name": "Test" 17 | }, 18 | "person": { 19 | "name": "Test" 20 | }, 21 | "person_id": 1, 22 | "pinned_to_deal_flag": false, 23 | "pinned_to_organization_flag": false, 24 | "pinned_to_person_flag": false, 25 | "update_time": "2019-06-26 14:30:52", 26 | "user": { 27 | "email": "test@test.com", 28 | "icon_url": null, 29 | "is_you": true, 30 | "name": "Test user" 31 | }, 32 | "user_id": 9542701 33 | }, 34 | "event": "added.note", 35 | "matches_filters": null, 36 | "meta": { 37 | "action": "added", 38 | "company_id": 6340191, 39 | "host": "test-b3a96b.pipedrive.com", 40 | "id": 1, 41 | "is_bulk_update": false, 42 | "object": "note", 43 | "permitted_user_ids": [ 44 | "*" 45 | ], 46 | "pipedrive_service_name": false, 47 | "timestamp": 1561559452, 48 | "timestamp_micro": 1561559452120913, 49 | "trans_pending": false, 50 | "user_id": 9542701, 51 | "v": 1, 52 | "webhook_id": "73109" 53 | }, 54 | "previous": null, 55 | "retry": 0, 56 | "v": 1 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /sources/pipedrive/webhook-examples/organizationAdded.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "current": { 5 | "active_flag": true, 6 | "activities_count": 0, 7 | "add_time": "2019-06-26 14:29:52", 8 | "address": null, 9 | "address_admin_area_level_1": null, 10 | "address_admin_area_level_2": null, 11 | "address_country": null, 12 | "address_formatted_address": null, 13 | "address_lat": null, 14 | "address_locality": null, 15 | "address_long": null, 16 | "address_postal_code": null, 17 | "address_route": null, 18 | "address_street_number": null, 19 | "address_sublocality": null, 20 | "address_subpremise": null, 21 | "category_id": null, 22 | "cc_email": "test-b3a96b@pipedrivemail.com", 23 | "closed_deals_count": 0, 24 | "company_id": 6340191, 25 | "country_code": null, 26 | "done_activities_count": 0, 27 | "edit_name": true, 28 | "email_messages_count": 0, 29 | "files_count": 0, 30 | "first_char": "t", 31 | "followers_count": 0, 32 | "id": 1, 33 | "label": null, 34 | "last_activity_date": null, 35 | "last_activity_id": null, 36 | "lost_deals_count": 0, 37 | "name": "Test", 38 | "next_activity_date": null, 39 | "next_activity_id": null, 40 | "next_activity_time": null, 41 | "notes_count": 0, 42 | "open_deals_count": 0, 43 | "owner_id": 9542701, 44 | "owner_name": "Test user", 45 | "people_count": 0, 46 | "picture_id": null, 47 | "reference_activities_count": 0, 48 | "related_closed_deals_count": 0, 49 | "related_lost_deals_count": 0, 50 | "related_open_deals_count": 0, 51 | "related_won_deals_count": 0, 52 | "undone_activities_count": 0, 53 | "update_time": "2019-06-26 14:29:52", 54 | "visible_to": "3", 55 | "won_deals_count": 0 56 | }, 57 | "event": "added.organization", 58 | "matches_filters": { 59 | "current": [] 60 | }, 61 | "meta": { 62 | "action": "added", 63 | "company_id": 6340191, 64 | "host": "test-b3a96b.pipedrive.com", 65 | "id": 1, 66 | "is_bulk_update": false, 67 | "matches_filters": { 68 | "current": [] 69 | }, 70 | "object": "organization", 71 | "permitted_user_ids": [ 72 | 9542701 73 | ], 74 | "pipedrive_service_name": false, 75 | "timestamp": 1561559392, 76 | "timestamp_micro": 1561559392126312, 77 | "trans_pending": false, 78 | "user_id": 9542701, 79 | "v": 1, 80 | "webhook_id": "73109" 81 | }, 82 | "previous": null, 83 | "retry": 0, 84 | "v": 1 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /sources/pipedrive/webhook-examples/organizationUpdated.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "current": { 5 | "active_flag": true, 6 | "activities_count": 0, 7 | "add_time": "2019-06-26 14:29:52", 8 | "address": null, 9 | "address_admin_area_level_1": null, 10 | "address_admin_area_level_2": null, 11 | "address_country": null, 12 | "address_formatted_address": null, 13 | "address_lat": null, 14 | "address_locality": null, 15 | "address_long": null, 16 | "address_postal_code": null, 17 | "address_route": null, 18 | "address_street_number": null, 19 | "address_sublocality": null, 20 | "address_subpremise": null, 21 | "category_id": null, 22 | "cc_email": "test-b3a96b@pipedrivemail.com", 23 | "closed_deals_count": 0, 24 | "company_id": 6340191, 25 | "country_code": null, 26 | "done_activities_count": 0, 27 | "edit_name": true, 28 | "email_messages_count": 0, 29 | "files_count": 0, 30 | "first_char": "t", 31 | "followers_count": 1, 32 | "id": 1, 33 | "label": null, 34 | "last_activity_date": null, 35 | "last_activity_id": null, 36 | "lost_deals_count": 0, 37 | "name": "Test", 38 | "next_activity_date": null, 39 | "next_activity_id": null, 40 | "next_activity_time": null, 41 | "notes_count": 1, 42 | "open_deals_count": 1, 43 | "owner_id": 9542701, 44 | "owner_name": "Test User", 45 | "people_count": 1, 46 | "picture_id": null, 47 | "reference_activities_count": 0, 48 | "related_closed_deals_count": 0, 49 | "related_lost_deals_count": 0, 50 | "related_open_deals_count": 0, 51 | "related_won_deals_count": 0, 52 | "undone_activities_count": 0, 53 | "update_time": "2019-06-26 14:31:14", 54 | "visible_to": "3", 55 | "won_deals_count": 0 56 | }, 57 | "event": "updated.organization", 58 | "matches_filters": { 59 | "current": [] 60 | }, 61 | "meta": { 62 | "action": "updated", 63 | "company_id": 6340191, 64 | "host": "test-b3a96b.pipedrive.com", 65 | "id": 1, 66 | "is_bulk_update": false, 67 | "matches_filters": { 68 | "current": [] 69 | }, 70 | "object": "organization", 71 | "permitted_user_ids": [ 72 | 9542701 73 | ], 74 | "pipedrive_service_name": false, 75 | "timestamp": 1561559474, 76 | "timestamp_micro": 1561559474443558, 77 | "trans_pending": false, 78 | "user_id": 9542701, 79 | "v": 1, 80 | "webhook_id": "73109" 81 | }, 82 | "previous": { 83 | "active_flag": true, 84 | "activities_count": 0, 85 | "add_time": "2019-06-26 14:29:52", 86 | "address": null, 87 | "address_admin_area_level_1": null, 88 | "address_admin_area_level_2": null, 89 | "address_country": null, 90 | "address_formatted_address": null, 91 | "address_lat": null, 92 | "address_locality": null, 93 | "address_long": null, 94 | "address_postal_code": null, 95 | "address_route": null, 96 | "address_street_number": null, 97 | "address_sublocality": null, 98 | "address_subpremise": null, 99 | "category_id": null, 100 | "cc_email": "test-b3a96b@pipedrivemail.com", 101 | "closed_deals_count": 0, 102 | "company_id": 6340191, 103 | "country_code": null, 104 | "done_activities_count": 0, 105 | "edit_name": true, 106 | "email_messages_count": 0, 107 | "files_count": 0, 108 | "first_char": "t", 109 | "followers_count": 1, 110 | "id": 1, 111 | "label": null, 112 | "last_activity_date": null, 113 | "last_activity_id": null, 114 | "lost_deals_count": 0, 115 | "name": "Test", 116 | "next_activity_date": null, 117 | "next_activity_id": null, 118 | "next_activity_time": null, 119 | "notes_count": 1, 120 | "open_deals_count": 0, 121 | "owner_id": 9542701, 122 | "owner_name": "Test User", 123 | "people_count": 1, 124 | "picture_id": null, 125 | "reference_activities_count": 0, 126 | "related_closed_deals_count": 0, 127 | "related_lost_deals_count": 0, 128 | "related_open_deals_count": 0, 129 | "related_won_deals_count": 0, 130 | "undone_activities_count": 0, 131 | "update_time": "2019-06-26 14:30:52", 132 | "visible_to": "3", 133 | "won_deals_count": 0 134 | }, 135 | "retry": 0, 136 | "v": 1 137 | } 138 | } 139 | } -------------------------------------------------------------------------------- /sources/pipedrive/webhook-examples/personAdded.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "current": { 5 | "active_flag": true, 6 | "activities_count": 0, 7 | "add_time": "2019-06-26 14:29:52", 8 | "cc_email": "test-b3a96b@pipedrivemail.com", 9 | "closed_deals_count": 0, 10 | "company_id": 6340191, 11 | "done_activities_count": 0, 12 | "email": [ 13 | { 14 | "primary": true, 15 | "value": "" 16 | } 17 | ], 18 | "email_messages_count": 0, 19 | "files_count": 0, 20 | "first_char": "t", 21 | "first_name": "Test", 22 | "followers_count": 0, 23 | "id": 1, 24 | "label": null, 25 | "last_activity_date": null, 26 | "last_activity_id": null, 27 | "last_incoming_mail_time": null, 28 | "last_name": null, 29 | "last_outgoing_mail_time": null, 30 | "lost_deals_count": 0, 31 | "name": "Test", 32 | "next_activity_date": null, 33 | "next_activity_id": null, 34 | "next_activity_time": null, 35 | "notes_count": 0, 36 | "open_deals_count": 0, 37 | "org_id": 1, 38 | "org_name": "Test", 39 | "owner_id": 9542701, 40 | "owner_name": "Test user", 41 | "participant_closed_deals_count": 0, 42 | "participant_open_deals_count": 0, 43 | "phone": [ 44 | { 45 | "primary": true, 46 | "value": "" 47 | } 48 | ], 49 | "picture_id": null, 50 | "reference_activities_count": 0, 51 | "related_closed_deals_count": 0, 52 | "related_lost_deals_count": 0, 53 | "related_open_deals_count": 0, 54 | "related_won_deals_count": 0, 55 | "undone_activities_count": 0, 56 | "update_time": "2019-06-26 14:29:52", 57 | "visible_to": "3", 58 | "won_deals_count": 0 59 | }, 60 | "event": "added.person", 61 | "matches_filters": { 62 | "current": [] 63 | }, 64 | "meta": { 65 | "action": "added", 66 | "company_id": 6340191, 67 | "host": "test-b3a96b.pipedrive.com", 68 | "id": 1, 69 | "is_bulk_update": false, 70 | "matches_filters": { 71 | "current": [] 72 | }, 73 | "object": "person", 74 | "permitted_user_ids": [ 75 | 9542701 76 | ], 77 | "pipedrive_service_name": false, 78 | "timestamp": 1561559392, 79 | "timestamp_micro": 1561559392306662, 80 | "trans_pending": false, 81 | "user_id": 9542701, 82 | "v": 1, 83 | "webhook_id": "73109" 84 | }, 85 | "previous": null, 86 | "retry": 0, 87 | "v": 1 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /sources/pipedrive/webhook-examples/personUpdated.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "current": { 5 | "active_flag": true, 6 | "activities_count": 0, 7 | "add_time": "2019-06-26 14:29:52", 8 | "cc_email": "test-b3a96b@pipedrivemail.com", 9 | "closed_deals_count": 0, 10 | "company_id": 6340191, 11 | "done_activities_count": 0, 12 | "email": [ 13 | { 14 | "primary": true, 15 | "value": "" 16 | } 17 | ], 18 | "email_messages_count": 0, 19 | "files_count": 0, 20 | "first_char": "t", 21 | "first_name": "Test", 22 | "followers_count": 1, 23 | "id": 1, 24 | "label": null, 25 | "last_activity_date": null, 26 | "last_activity_id": null, 27 | "last_incoming_mail_time": null, 28 | "last_name": null, 29 | "last_outgoing_mail_time": null, 30 | "lost_deals_count": 0, 31 | "name": "Test", 32 | "next_activity_date": null, 33 | "next_activity_id": null, 34 | "next_activity_time": null, 35 | "notes_count": 1, 36 | "open_deals_count": 1, 37 | "org_id": 1, 38 | "org_name": "Test", 39 | "owner_id": 9542701, 40 | "owner_name": "Test user", 41 | "participant_closed_deals_count": 0, 42 | "participant_open_deals_count": 0, 43 | "phone": [ 44 | { 45 | "primary": true, 46 | "value": "" 47 | } 48 | ], 49 | "picture_id": null, 50 | "reference_activities_count": 0, 51 | "related_closed_deals_count": 0, 52 | "related_lost_deals_count": 0, 53 | "related_open_deals_count": 0, 54 | "related_won_deals_count": 0, 55 | "undone_activities_count": 0, 56 | "update_time": "2019-06-26 14:31:14", 57 | "visible_to": "3", 58 | "won_deals_count": 0 59 | }, 60 | "event": "updated.person", 61 | "matches_filters": { 62 | "current": [] 63 | }, 64 | "meta": { 65 | "action": "updated", 66 | "company_id": 6340191, 67 | "host": "test-b3a96b.pipedrive.com", 68 | "id": 1, 69 | "is_bulk_update": false, 70 | "matches_filters": { 71 | "current": [] 72 | }, 73 | "object": "person", 74 | "permitted_user_ids": [ 75 | 9542701 76 | ], 77 | "pipedrive_service_name": false, 78 | "timestamp": 1561559474, 79 | "timestamp_micro": 1561559474460669, 80 | "trans_pending": false, 81 | "user_id": 9542701, 82 | "v": 1, 83 | "webhook_id": "73109" 84 | }, 85 | "previous": { 86 | "active_flag": true, 87 | "activities_count": 0, 88 | "add_time": "2019-06-26 14:29:52", 89 | "cc_email": "test-b3a96b@pipedrivemail.com", 90 | "closed_deals_count": 0, 91 | "company_id": 6340191, 92 | "done_activities_count": 0, 93 | "email": [ 94 | { 95 | "primary": true, 96 | "value": "" 97 | } 98 | ], 99 | "email_messages_count": 0, 100 | "files_count": 0, 101 | "first_char": "t", 102 | "first_name": "Test", 103 | "followers_count": 1, 104 | "id": 1, 105 | "label": null, 106 | "last_activity_date": null, 107 | "last_activity_id": null, 108 | "last_incoming_mail_time": null, 109 | "last_name": null, 110 | "last_outgoing_mail_time": null, 111 | "lost_deals_count": 0, 112 | "name": "Test", 113 | "next_activity_date": null, 114 | "next_activity_id": null, 115 | "next_activity_time": null, 116 | "notes_count": 1, 117 | "open_deals_count": 0, 118 | "org_id": 1, 119 | "org_name": "Test", 120 | "owner_id": 9542701, 121 | "owner_name": "Test user", 122 | "participant_closed_deals_count": 0, 123 | "participant_open_deals_count": 0, 124 | "phone": [ 125 | { 126 | "primary": true, 127 | "value": "" 128 | } 129 | ], 130 | "picture_id": null, 131 | "reference_activities_count": 0, 132 | "related_closed_deals_count": 0, 133 | "related_lost_deals_count": 0, 134 | "related_open_deals_count": 0, 135 | "related_won_deals_count": 0, 136 | "undone_activities_count": 0, 137 | "update_time": "2019-06-26 14:30:52", 138 | "visible_to": "3", 139 | "won_deals_count": 0 140 | }, 141 | "retry": 0, 142 | "v": 1 143 | } 144 | } 145 | } -------------------------------------------------------------------------------- /sources/sendgrid/readme.md: -------------------------------------------------------------------------------- 1 | 2 |

3 | This is a source function template that subscribes to Sendgrid via Sendgrid Webhooks. 4 |

5 | To configure, create event webhooks within your Sendgrid instance and point them to the source URL found your Segment source function. 6 |

7 | FYI, Segment also has a native Sendgrid Object Source integration (which pulls aggregate data from Sendgrid every 3 hours and moves to your Data Warehouse). 8 | -------------------------------------------------------------------------------- /sources/shippo/handler.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Please do not delete [used for Intellisense] 3 | * @param {ServerRequest} request The incoming webhook request 4 | * @param {Object.} settings Custom settings 5 | * @return void 6 | */ 7 | async function onRequest(request, settings) { 8 | // exports.processEvents = async (event) => { 9 | const body = request.json() 10 | const { data } = body; 11 | const { tracking_status } = data; 12 | const properties = getProperties(body); 13 | Segment.set({ 14 | collection: 'shipment', 15 | id: tracking_status.object_id, 16 | properties 17 | }) 18 | } 19 | 20 | /** 21 | * @param {String} type e.g. `from` or `to`. 22 | * @param {Object} address Object with different properties of address. Keys here are `zip`, `country`, etc. 23 | * @returns {Object} Where each key in `address` is prefixed by `type`. 24 | */ 25 | function getTypedAddress(type, address) { 26 | const ret = {}; 27 | Object.keys(address).forEach((key) => { 28 | ret[`${type}_${key}`] = address[key]; 29 | }) 30 | return ret; 31 | } 32 | 33 | /** 34 | * @param {Object} body from Shippo. 35 | * @returns {Object} properties for Segment to put in the `properties` field. 36 | */ 37 | function getProperties(body) { 38 | const { carrier, data } = body; 39 | const { tracking_status, address_from, address_to, tracking_number, eta, original_eta } = data; 40 | return { 41 | carrier, 42 | eta, 43 | original_eta, 44 | tracking_number, 45 | status: tracking_status.status, 46 | ...getTypedAddress('from', address_from), 47 | ...getTypedAddress('to', address_to) 48 | } 49 | } 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /sources/shippo/webhook-examples/delivered.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "carrier": "shippo", 5 | "metadata": "Shippo test webhook", 6 | "test": true, 7 | "data": { 8 | "eta": "2019-06-26T23:51:11.965Z", 9 | "test": true, 10 | "original_eta": "2019-06-25T23:51:11.965Z", 11 | "tracking_status": { 12 | "status_date": "2019-06-26T21:46:11.974Z", 13 | "location": { 14 | "zip": "60611", 15 | "country": "US", 16 | "city": "Chicago", 17 | "state": "IL" 18 | }, 19 | "object_created": "2019-06-27T23:51:11.974Z", 20 | "object_id": "93c729ab8c824cb6a8c74eea1b4adfce", 21 | "status_details": "Your shipment has been delivered.", 22 | "status": "DELIVERED" 23 | }, 24 | "address_to": { 25 | "zip": "60611", 26 | "country": "US", 27 | "city": "Chicago", 28 | "state": "IL" 29 | }, 30 | "messages": [ 31 | ], 32 | "tracking_number": "SHIPPO_DELIVERED", 33 | "servicelevel": { 34 | "token": "shippo_priority" 35 | }, 36 | "tracking_history": [ 37 | { 38 | "status_date": "2019-06-23T15:31:11.977Z", 39 | "location": { 40 | "zip": "94103", 41 | "country": "US", 42 | "city": "San Francisco", 43 | "state": "CA" 44 | }, 45 | "object_created": "2019-06-27T23:51:11.977Z", 46 | "object_id": "e0123d4b65d54f42bfdf388c27a9cf20", 47 | "status_details": "The carrier has received the electronic shipment information.", 48 | "status": "UNKNOWN" 49 | }, 50 | { 51 | "status_date": "2019-06-24T17:36:11.977Z", 52 | "location": { 53 | "zip": "94103", 54 | "country": "US", 55 | "city": "San Francisco", 56 | "state": "CA" 57 | }, 58 | "object_created": "2019-06-27T23:51:11.977Z", 59 | "object_id": "df04685b7d7c4b76b2f8dc09d2509c6e", 60 | "status_details": "Your shipment has departed from the origin.", 61 | "status": "TRANSIT" 62 | }, 63 | { 64 | "status_date": "2019-06-25T19:41:11.977Z", 65 | "location": { 66 | "zip": "37501", 67 | "country": "US", 68 | "city": "Memphis", 69 | "state": "TN" 70 | }, 71 | "object_created": "2019-06-27T23:51:11.977Z", 72 | "object_id": "767e23d8462440f0b4a7cfd3ff6a256d", 73 | "status_details": "The Postal Service has identified a problem with the processing of this item and you should contact support to get further information.", 74 | "status": "FAILURE" 75 | }, 76 | { 77 | "status_date": "2019-06-26T21:46:11.977Z", 78 | "location": { 79 | "zip": "60611", 80 | "country": "US", 81 | "city": "Chicago", 82 | "state": "IL" 83 | }, 84 | "object_created": "2019-06-27T23:51:11.977Z", 85 | "object_id": "fe66a6e8b69c4704b9bef530241b5f8d", 86 | "status_details": "Your shipment has been delivered.", 87 | "status": "DELIVERED" 88 | } 89 | ], 90 | "address_from": { 91 | "zip": "94103", 92 | "country": "US", 93 | "city": "San Francisco", 94 | "state": "CA" 95 | } 96 | }, 97 | "event": "track_updated" 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /sources/shopify/handler.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Please do not delete [used for Intellisense] 3 | * @param {ServerRequest} request The incoming webhook request 4 | * @param {Object.} settings Custom settings 5 | * @return void 6 | */ 7 | async function onRequest(request, settings) { 8 | // exports.processEvents = async (event) => { 9 | let eventBody = request.json(); 10 | 11 | const sp = await payload(eventBody) 12 | const eventName = await setEventName(eventBody) 13 | const order = eventBody 14 | 15 | Segment.track({ 16 | event: eventName, 17 | userId: String(order.customer.id), 18 | properties: sp 19 | }) 20 | } 21 | 22 | //--------------------------------------------------------------------- 23 | // HELPERS 24 | //--------------------------------------------------------------------- 25 | const setEventName = async order => { 26 | const financial_status = order.financial_status; 27 | const fulfillment_status = order.fulfillment_status; 28 | 29 | const eventName = function() { 30 | return financial_status === "refunded" ? "Order Refunded" 31 | : financial_status === "voided" ? "Order Cancelled" 32 | : financial_status === "paid" & fulfillment_status === "fulfilled" ? "Order Completed" 33 | : "Order Updated" 34 | } 35 | return eventName(); 36 | } 37 | 38 | 39 | // helper function to build track call properties based on order and products data 40 | const payload = async order => { 41 | const line_items = order.line_items; 42 | 43 | const sp = {}; 44 | sp['order_id'] = order.id; 45 | sp['order_number'] = order.order_number; 46 | sp['number'] = order.number; 47 | sp['financial_status'] = order.financial_status; 48 | sp['fulfillment_status'] = order.fulfillment_status; 49 | sp['currency'] = order.currency; 50 | sp['total_price'] = order.total_price; 51 | sp['subtotal_price'] = order.subtotal_price; 52 | sp['total_tax'] = order.total_tax; 53 | sp['total_discounts'] = order.total_discounts; 54 | sp['total_line_items_price'] = order.total_line_items_price; 55 | sp['order_name'] = order.name; 56 | sp['customer_first_name'] = order.customer.first_name; 57 | sp['customer_last_name'] = order.customer.last_name; 58 | sp['customer_email'] = order.email; 59 | sp['products'] = []; 60 | const len = line_items.length; 61 | for (var i = 0; i < len; i++) { 62 | sp['products'].push({ 63 | productId: line_items[i].product_id, 64 | title: line_items[i].title, 65 | sku: line_items[i].sku, 66 | quantity: line_items[i].quantity, 67 | price: line_items[i].price, 68 | variant_id: line_items[i].variant_id, 69 | variant_title: line_items[i].variant_title, 70 | vendor: line_items[i].variant, 71 | requires_shipping: line_items[i].requires_shipping, 72 | taxable: line_items[i].taxable, 73 | name: line_items[i].name 74 | 75 | }); 76 | } 77 | return sp; 78 | } 79 | -------------------------------------------------------------------------------- /sources/stripe-events/handler.js: -------------------------------------------------------------------------------- 1 | async function onRequest(request, settings) { 2 | let eventBody = request.json(); 3 | 4 | if (eventBody.type == "charge.succeeded" && eventBody.data.object.customer) { 5 | Segment.track({ 6 | event: "Charge Succeeded", 7 | userId: eventBody.data.object.customer, 8 | properties: { 9 | createTime: eventBody.data.object.created, 10 | amount: eventBody.data.object.amount, 11 | paid: eventBody.data.object.paid, 12 | status: eventBody.data.object.status 13 | }, 14 | context: { 15 | source: "Stripe" 16 | } 17 | }) 18 | } 19 | 20 | if (eventBody.type == "customer.created") { 21 | Segment.track({ 22 | event: "Customer Created", 23 | userId: eventBody.data.object.id, 24 | properties: { 25 | createTime: eventBody.data.object.created, 26 | account_balance: eventBody.data.object.account_balance, 27 | currency: eventBody.data.object.currency 28 | }, 29 | context: { 30 | source: "stripe" 31 | } 32 | }) 33 | 34 | Segment.identify({ 35 | userId: eventBody.data.object.id, 36 | properties: { 37 | createTime: eventBody.data.object.created, 38 | account_balance: eventBody.data.object.account_balance, 39 | currency: eventBody.data.object.currency 40 | }, 41 | context: { 42 | source: "Stripe" 43 | } 44 | }) 45 | } 46 | 47 | if (eventBody.type == "customer.subscription.created") { 48 | Segment.track({ 49 | event: "Subscription Created", 50 | userId: eventBody.data.object.customer, 51 | properties: { 52 | createTime: eventBody.data.object.created, 53 | plan_id: eventBody.data.object.plan.id, 54 | plan_name: eventBody.data.object.plan.name, 55 | plan_interval: eventBody.data.object.plan.interval, 56 | trial_start: eventBody.data.object.trial_start, 57 | trial_end: eventBody.data.object.trial_end 58 | }, 59 | context: { 60 | source: "stripe" 61 | } 62 | }) 63 | } 64 | 65 | if (eventBody.type == "customer.subscription.trial_will_end") { 66 | Segment.track({ 67 | event: "Trial Ending Soon", 68 | userId: eventBody.data.object.customer, 69 | properties: { 70 | plan_id: eventBody.data.object.plan.id, 71 | plan_name: eventBody.data.object.plan.name, 72 | plan_interval: eventBody.data.object.plan.interval, 73 | trial_start: eventBody.data.object.trial_start, 74 | trial_end: eventBody.data.object.trial_end 75 | }, 76 | context: { 77 | source: "stripe" 78 | } 79 | }) 80 | } 81 | } -------------------------------------------------------------------------------- /sources/stripe-events/webhook-examples/charge-succeeded.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "headers": {}, 4 | "queryParams": {}, 5 | "body": { 6 | "created": 1326853478, 7 | "livemode": false, 8 | "id": "evt_00000000000000", 9 | "type": "charge.succeeded", 10 | "object": "event", 11 | "request": null, 12 | "pending_webhooks": 1, 13 | "api_version": "2015-10-16", 14 | "data": { 15 | "object": { 16 | "id": "ch_00000000000000", 17 | "object": "charge", 18 | "amount": 849, 19 | "amount_refunded": 0, 20 | "application": null, 21 | "application_fee": null, 22 | "application_fee_amount": null, 23 | "balance_transaction": "txn_00000000000000", 24 | "billing_details": { 25 | "address": { 26 | "city": null, 27 | "country": null, 28 | "line1": null, 29 | "line2": null, 30 | "postal_code": null, 31 | "state": null 32 | }, 33 | "email": null, 34 | "name": null, 35 | "phone": null 36 | }, 37 | "captured": true, 38 | "created": 1470077924, 39 | "currency": "usd", 40 | "customer": null, 41 | "description": null, 42 | "destination": null, 43 | "dispute": null, 44 | "failure_code": null, 45 | "failure_message": null, 46 | "fraud_details": {}, 47 | "invoice": null, 48 | "livemode": false, 49 | "metadata": {}, 50 | "on_behalf_of": null, 51 | "order": null, 52 | "outcome": { 53 | "network_status": "approved_by_network", 54 | "reason": null, 55 | "risk_level": "normal", 56 | "seller_message": "Payment complete.", 57 | "type": "authorized" 58 | }, 59 | "paid": true, 60 | "payment_intent": null, 61 | "payment_method": "card_00000000000000", 62 | "payment_method_details": { 63 | "card": { 64 | "brand": "visa", 65 | "checks": { 66 | "address_line1_check": null, 67 | "address_postal_code_check": null, 68 | "cvc_check": "pass" 69 | }, 70 | "country": "US", 71 | "exp_month": 5, 72 | "exp_year": 2019, 73 | "fingerprint": "FZhQ9QLHoLLhPwq4", 74 | "funding": "credit", 75 | "last4": "4242", 76 | "three_d_secure": null, 77 | "wallet": null 78 | }, 79 | "type": "card" 80 | }, 81 | "receipt_email": null, 82 | "receipt_number": null, 83 | "receipt_url": 84 | "https://pay.stripe.com/receipts/acct_170xf8DJPTOKB2cy/ch_18dlceDJPTOKB2cy0RMNOfxB/rcpt_EHp4VuHC9tTnYfGCyD1ffQin0TY4Vcc", 85 | "refunded": false, 86 | "refunds": { 87 | "object": "list", 88 | "data": [], 89 | "has_more": false, 90 | "total_count": 0, 91 | "url": "/v1/charges/ch_18dlceDJPTOKB2cy0RMNOfxB/refunds" 92 | }, 93 | "review": null, 94 | "shipping": null, 95 | "source": { 96 | "id": "card_00000000000000", 97 | "object": "card", 98 | "address_city": null, 99 | "address_country": null, 100 | "address_line1": null, 101 | "address_line1_check": null, 102 | "address_line2": null, 103 | "address_state": null, 104 | "address_zip": null, 105 | "address_zip_check": null, 106 | "brand": "Visa", 107 | "country": "US", 108 | "customer": null, 109 | "cvc_check": "pass", 110 | "dynamic_last4": null, 111 | "exp_month": 5, 112 | "exp_year": 2019, 113 | "fingerprint": "FZhQ9QLHoLLhPwq4", 114 | "funding": "credit", 115 | "last4": "4242", 116 | "metadata": {}, 117 | "name": null, 118 | "tokenization_method": null 119 | }, 120 | "source_transfer": null, 121 | "statement_descriptor": null, 122 | "status": "succeeded", 123 | "transfer_data": null, 124 | "transfer_group": null 125 | } 126 | } 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /sources/stripe-events/webhook-examples/customer-created.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "headers": {}, 4 | "queryParams": {}, 5 | "body": { 6 | "created": 1326853478, 7 | "livemode": false, 8 | "id": "evt_00000000000000", 9 | "type": "customer.created", 10 | "object": "event", 11 | "request": null, 12 | "pending_webhooks": 1, 13 | "api_version": "2015-10-16", 14 | "data": { 15 | "object": { 16 | "id": "cus_00000000000000", 17 | "object": "customer", 18 | "account_balance": 0, 19 | "address": null, 20 | "balance": 0, 21 | "created": 1561487054, 22 | "currency": "usd", 23 | "default_source": null, 24 | "delinquent": false, 25 | "description": null, 26 | "discount": null, 27 | "email": null, 28 | "invoice_prefix": "8244112", 29 | "invoice_settings": { 30 | "custom_fields": null, 31 | "default_payment_method": null, 32 | "footer": null 33 | }, 34 | "livemode": false, 35 | "metadata": {}, 36 | "name": null, 37 | "phone": null, 38 | "preferred_locales": [], 39 | "shipping": null, 40 | "sources": { 41 | "object": "list", 42 | "data": [], 43 | "has_more": false, 44 | "total_count": 0, 45 | "url": "/v1/customers/cus_FJxDxag22STS9B/sources" 46 | }, 47 | "subscriptions": { 48 | "object": "list", 49 | "data": [], 50 | "has_more": false, 51 | "total_count": 0, 52 | "url": "/v1/customers/cus_FJxDxag22STS9B/subscriptions" 53 | }, 54 | "tax_exempt": "none", 55 | "tax_ids": { 56 | "object": "list", 57 | "data": [], 58 | "has_more": false, 59 | "total_count": 0, 60 | "url": "/v1/customers/cus_FJxDxag22STS9B/tax_ids" 61 | }, 62 | "tax_info": null, 63 | "tax_info_verification": null 64 | } 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /sources/stripe-events/webhook-examples/subscription-created.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "headers": {}, 4 | "queryParams": {}, 5 | "body": { 6 | "created": 1326853478, 7 | "livemode": false, 8 | "id": "evt_00000000000000", 9 | "type": "customer.subscription.created", 10 | "object": "event", 11 | "request": null, 12 | "pending_webhooks": 1, 13 | "api_version": "2015-10-16", 14 | "data": { 15 | "object": { 16 | "id": "sub_00000000000000", 17 | "object": "subscription", 18 | "application_fee_percent": null, 19 | "billing": "charge_automatically", 20 | "billing_cycle_anchor": 1466874758, 21 | "billing_thresholds": null, 22 | "cancel_at": null, 23 | "cancel_at_period_end": false, 24 | "canceled_at": 1466814948, 25 | "collection_method": "charge_automatically", 26 | "created": 1466788358, 27 | "current_period_end": 1466874758, 28 | "current_period_start": 1466788358, 29 | "customer": "cus_00000000000000", 30 | "days_until_due": null, 31 | "default_payment_method": null, 32 | "default_source": null, 33 | "default_tax_rates": [], 34 | "discount": null, 35 | "ended_at": 1466814948, 36 | "items": { 37 | "object": "list", 38 | "data": [ 39 | { 40 | "id": "si_00000000000000", 41 | "object": "subscription_item", 42 | "billing_thresholds": null, 43 | "created": 1466788359, 44 | "metadata": {}, 45 | "plan": { 46 | "id": "team-$100-1-month_00000000000000", 47 | "object": "plan", 48 | "active": true, 49 | "aggregate_usage": null, 50 | "amount": 1, 51 | "billing_scheme": "per_unit", 52 | "created": 1466788217, 53 | "currency": "usd", 54 | "interval": "month", 55 | "interval_count": 1, 56 | "livemode": false, 57 | "metadata": {}, 58 | "name": "Team", 59 | "nickname": null, 60 | "product": "prod_00000000000000", 61 | "statement_descriptor": "Segment Team", 62 | "tiers": null, 63 | "tiers_mode": null, 64 | "transform_usage": null, 65 | "trial_period_days": 1, 66 | "usage_type": "licensed" 67 | }, 68 | "quantity": 1, 69 | "subscription": "sub_00000000000000" 70 | } 71 | ], 72 | "has_more": false, 73 | "total_count": 1, 74 | "url": "/v1/subscription_items?subscription=sub_8hMaOQeuVQMqgI" 75 | }, 76 | "latest_invoice": null, 77 | "livemode": false, 78 | "metadata": {}, 79 | "plan": { 80 | "id": "team-$100-1-month_00000000000000", 81 | "object": "plan", 82 | "active": true, 83 | "aggregate_usage": null, 84 | "amount": 1, 85 | "billing_scheme": "per_unit", 86 | "created": 1466788217, 87 | "currency": "usd", 88 | "interval": "month", 89 | "interval_count": 1, 90 | "livemode": false, 91 | "metadata": {}, 92 | "name": "Team", 93 | "nickname": null, 94 | "product": "prod_00000000000000", 95 | "statement_descriptor": "Segment Team", 96 | "tiers": null, 97 | "tiers_mode": null, 98 | "transform_usage": null, 99 | "trial_period_days": 1, 100 | "usage_type": "licensed" 101 | }, 102 | "quantity": 1, 103 | "schedule": null, 104 | "start": 1466788358, 105 | "start_date": 1466788358, 106 | "status": "canceled", 107 | "tax_percent": null, 108 | "trial_end": 1466874758, 109 | "trial_start": 1466788358 110 | } 111 | } 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /sources/stripe-events/webhook-examples/trial-will-end-soon.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "headers": {}, 4 | "queryParams": {}, 5 | "body": { 6 | "created": 1326853478, 7 | "livemode": false, 8 | "id": "evt_00000000000000", 9 | "type": "customer.subscription.trial_will_end", 10 | "object": "event", 11 | "request": null, 12 | "pending_webhooks": 1, 13 | "api_version": "2015-10-16", 14 | "data": { 15 | "object": { 16 | "id": "sub_00000000000000", 17 | "object": "subscription", 18 | "application_fee_percent": null, 19 | "billing": "charge_automatically", 20 | "billing_cycle_anchor": 1466874758, 21 | "billing_thresholds": null, 22 | "cancel_at": null, 23 | "cancel_at_period_end": false, 24 | "canceled_at": 1466814948, 25 | "collection_method": "charge_automatically", 26 | "created": 1466788358, 27 | "current_period_end": 1466874758, 28 | "current_period_start": 1466788358, 29 | "customer": "cus_00000000000000", 30 | "days_until_due": null, 31 | "default_payment_method": null, 32 | "default_source": null, 33 | "default_tax_rates": [], 34 | "discount": null, 35 | "ended_at": 1466814948, 36 | "items": { 37 | "object": "list", 38 | "data": [ 39 | { 40 | "id": "si_00000000000000", 41 | "object": "subscription_item", 42 | "billing_thresholds": null, 43 | "created": 1466788359, 44 | "metadata": {}, 45 | "plan": { 46 | "id": "team-$100-1-month_00000000000000", 47 | "object": "plan", 48 | "active": true, 49 | "aggregate_usage": null, 50 | "amount": 1, 51 | "billing_scheme": "per_unit", 52 | "created": 1466788217, 53 | "currency": "usd", 54 | "interval": "month", 55 | "interval_count": 1, 56 | "livemode": false, 57 | "metadata": {}, 58 | "name": "Team", 59 | "nickname": null, 60 | "product": "prod_00000000000000", 61 | "statement_descriptor": "Segment Team", 62 | "tiers": null, 63 | "tiers_mode": null, 64 | "transform_usage": null, 65 | "trial_period_days": 1, 66 | "usage_type": "licensed" 67 | }, 68 | "quantity": 1, 69 | "subscription": "sub_00000000000000" 70 | } 71 | ], 72 | "has_more": false, 73 | "total_count": 1, 74 | "url": "/v1/subscription_items?subscription=sub_8hMaOQeuVQMqgI" 75 | }, 76 | "latest_invoice": null, 77 | "livemode": false, 78 | "metadata": {}, 79 | "plan": { 80 | "id": "team-$100-1-month_00000000000000", 81 | "object": "plan", 82 | "active": true, 83 | "aggregate_usage": null, 84 | "amount": 1, 85 | "billing_scheme": "per_unit", 86 | "created": 1466788217, 87 | "currency": "usd", 88 | "interval": "month", 89 | "interval_count": 1, 90 | "livemode": false, 91 | "metadata": {}, 92 | "name": "Team", 93 | "nickname": null, 94 | "product": "prod_00000000000000", 95 | "statement_descriptor": "Segment Team", 96 | "tiers": null, 97 | "tiers_mode": null, 98 | "transform_usage": null, 99 | "trial_period_days": 1, 100 | "usage_type": "licensed" 101 | }, 102 | "quantity": 1, 103 | "schedule": null, 104 | "start": 1466788358, 105 | "start_date": 1466788358, 106 | "status": "trialing", 107 | "tax_percent": null, 108 | "trial_end": 1561745950, 109 | "trial_start": 1561486750 110 | } 111 | } 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /sources/survey-monkey/handler.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Please do not delete [used for Intellisense] 3 | * @param {ServerRequest} request The incoming webhook request 4 | * @param {Object.} settings Custom settings 5 | * @return void 6 | */ 7 | async function onRequest(request, settings) { 8 | let eventBody = request.json(); 9 | 10 | Segment.track({ 11 | event: eventBody.event_type, 12 | userId: eventBody.resources.respondent_id, 13 | timestamp: eventBody.event_datetime, 14 | properties: { 15 | source: "SurveyMonkey", 16 | name: eventBody.name, 17 | objectType: eventBody.object_type, 18 | objectId: eventBody.object_id, 19 | filterId: eventBody.filter_id, 20 | eventId: eventBody.event_id, 21 | filterType: eventBody.filter_type, 22 | eventType: eventBody.event_type, 23 | subscriptionUrl: eventBody.subscription_url, 24 | href: eventBody.href, 25 | eventDateTime: eventBody.event_datetime, 26 | resources: { 27 | respondentId: eventBody.resources.respondent_id, 28 | recipientId: eventBody.resources.recipient_id, 29 | userId: eventBody.resources.user_id, 30 | collectorId: eventBody.resources.collector_id, 31 | surveyId: eventBody.resources.survey_id 32 | } 33 | } 34 | }) 35 | } -------------------------------------------------------------------------------- /sources/survey-monkey/webhook-examples/responseCompleted.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "event_datetime": "2019-06-25T22:21:35.296175+00:00", 5 | "resources": { 6 | "respondent_id": "10822661769", 7 | "recipient_id": "0", 8 | "user_id": "136503362", 9 | "collector_id": "239253167", 10 | "survey_id": "181192877" 11 | }, 12 | "name": "survey completed", 13 | "object_id": "10822661769", 14 | "filter_id": "181192877", 15 | "event_id": "10015473787", 16 | "object_type": "response", 17 | "filter_type": "survey", 18 | "event_type": "response_completed" 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /sources/talkable/handler.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Please do not delete [used for Intellisense] 3 | * @param {ServerRequest} request The incoming webhook request 4 | * @param {Object.} settings Custom settings 5 | * @return void 6 | */ 7 | async function onRequest(request, settings) { 8 | let eventBody = request.json() 9 | 10 | let returnValue = { 11 | objects: [], 12 | events: [] 13 | } 14 | let payload = eventBody.payload 15 | let eventObj = { 16 | properties: { ...payload, source: 'Talkable' } 17 | } 18 | 19 | switch (eventBody.type) { 20 | case 'unsubscribe_web_hook': 21 | eventObj.anonymousId = payload.person.email; 22 | eventObj.event = 'User Unsubscribed'; 23 | break; 24 | case 'reward_web_hook': 25 | eventObj.anonymousId = payload.advocate_origin.email; 26 | eventObj.event = 'Reward Triggered'; 27 | break; 28 | case 'check_unsubscribe_web_hook': 29 | eventObj.anonymousId = payload.recipient.email; 30 | eventObj.event = 'Unsubscribe Status Checked'; 31 | break; 32 | case 'claim_signup_web_hook': 33 | eventObj.anonymousId = payload.email; 34 | eventObj.event = 'Advocated Signed Up'; 35 | break; 36 | case 'event_web_hook': 37 | // this webhook is a bit generic, so using the event category 38 | eventObj.anonymousId = payload.person.email; 39 | eventObj.event = payload.origin.event_category || 'Triggered Event'; 40 | break; 41 | case 'offer_signup_web_hook': 42 | eventObj.anonymousId = payload.email; 43 | eventObj.event = 'Offer Signup'; 44 | break; 45 | case 'post_share_web_hook': 46 | eventObj.anonymousId = payload.origin.email; 47 | eventObj.event = 'Post Shared'; 48 | break; 49 | // COUPON not a user event, we should add this event to Coupon object 50 | // case 'create_coupon_web_hook': 51 | // 52 | // eventObj.event = 'Coupon Created'; 53 | // break; 54 | case 'referral_web_hook': 55 | eventObj.anonymousId = payload.referrer.email; 56 | eventObj.event = 'Referral Approved'; 57 | break; 58 | } 59 | 60 | // adding in email as an externalId 61 | eventObj.context = { externalIds: [{ 62 | id: eventObj.anonymousId, 63 | type: 'email', 64 | encoding: 'none', 65 | collection: 'users' 66 | }]} 67 | 68 | Segment.track(eventObj) 69 | } 70 | -------------------------------------------------------------------------------- /sources/talkable/webhook-examples/claim_signup_web_hook.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "payload": { 5 | "offer": { 6 | "email": "referrer@example.com", 7 | "short_url_code": "1a2PV", 8 | "ip_address": "127.0.0.1" 9 | }, 10 | "campaign": { 11 | "id": 465557015, 12 | "type": "StandaloneCampaign", 13 | "cached_slug": 465557015, 14 | "tag_names": [ 15 | "default" 16 | ], 17 | "origin_min_age": null, 18 | "origin_max_age": null, 19 | "new_customer": null 20 | }, 21 | "email": "john@example.com", 22 | "first_name": null, 23 | "last_name": null, 24 | "ip_address": "127.0.0.1", 25 | "gender": null, 26 | "sub_choice": true, 27 | "subscribed_at": "2019-06-27T10:34:36.026-07:00", 28 | "opted_in_at": "2019-06-27T10:34:36.026-07:00", 29 | "unsubscribed_at": null, 30 | "referral_counts": { 31 | "total": 0, 32 | "approved": 0, 33 | "pending": 0 34 | } 35 | }, 36 | "extras": {}, 37 | "key": "b3ab02149bd6aa9caefa7cad9e410236", 38 | "site": "segment-com", 39 | "type": "claim_signup_web_hook", 40 | "test": true 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /sources/talkable/webhook-examples/offer_signup_web_hook.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "payload": { 5 | "offer": { 6 | "email": "referrer@example.com", 7 | "short_url_code": "1a2PV", 8 | "ip_address": "127.0.0.1" 9 | }, 10 | "campaign": { 11 | "id": 190976863, 12 | "type": "StandaloneCampaign", 13 | "cached_slug": 190976863, 14 | "tag_names": [ 15 | "default" 16 | ], 17 | "origin_min_age": null, 18 | "origin_max_age": null, 19 | "new_customer": null 20 | }, 21 | "email": "john@example.com", 22 | "first_name": "John", 23 | "last_name": "Doe", 24 | "gender": null, 25 | "sub_choice": false, 26 | "subscribed_at": null, 27 | "opted_in_at": null, 28 | "unsubscribed_at": null, 29 | "referral_counts": { 30 | "total": 0, 31 | "approved": 0, 32 | "pending": 0 33 | } 34 | }, 35 | "extras": {}, 36 | "key": "b3ab02149bd6aa9caefa7cad9e410236", 37 | "site": "segment-com", 38 | "type": "offer_signup_web_hook", 39 | "test": true 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /sources/talkable/webhook-examples/post_share_web_hook.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "payload": { 5 | "campaign": { 6 | "id": 245711250, 7 | "type": "StandaloneCampaign", 8 | "cached_slug": 245711250, 9 | "tag_names": [ 10 | "default" 11 | ], 12 | "origin_min_age": null, 13 | "origin_max_age": null, 14 | "new_customer": null 15 | }, 16 | "share_type": "email", 17 | "share_info": { 18 | "recipients": [ 19 | "john@example.com" 20 | ] 21 | }, 22 | "origin": { 23 | "id": 787332138, 24 | "type": "Purchase", 25 | "email": "referrer@example.com" 26 | } 27 | }, 28 | "extras": {}, 29 | "key": "b3ab02149bd6aa9caefa7cad9e410236", 30 | "site": "segment-com", 31 | "type": "post_share_web_hook", 32 | "test": true 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /sources/talkable/webhook-examples/reward_web_hook.json: -------------------------------------------------------------------------------- 1 | { 2 | "payload": { 3 | "body": { 4 | "payload": { 5 | "person": null, 6 | "origin": { 7 | "id": 294603221, 8 | "type": "AffiliateMember", 9 | "email": "referrer@example.com", 10 | "customer_id": "793883414", 11 | "ip_address": "127.0.0.1" 12 | }, 13 | "advocate_origin": { 14 | "id": 294603221, 15 | "type": "AffiliateMember", 16 | "email": "referrer@example.com", 17 | "customer_id": "793883414", 18 | "ip_address": "127.0.0.1" 19 | }, 20 | "friend_origin": null, 21 | "campaign": { 22 | "id": 586902028, 23 | "type": "StandaloneCampaign", 24 | "cached_slug": 586902028, 25 | "tag_names": [ 26 | "default" 27 | ], 28 | "origin_min_age": null, 29 | "origin_max_age": null, 30 | "new_customer": null 31 | }, 32 | "offer": { 33 | "email": "referrer@example.com", 34 | "short_url_code": "1a2PV", 35 | "ip_address": "127.0.0.1" 36 | }, 37 | "reward": { 38 | "reason": "share", 39 | "incentive_type": "discount_coupon", 40 | "incentive_description": "shared coupon \"SAMPLE-COUPON-CODE\" for $10.00 off", 41 | "amount": null, 42 | "coupon_code": "SAMPLE-COUPON-CODE" 43 | } 44 | }, 45 | "extras": {}, 46 | "key": "b3ab02149bd6aa9caefa7cad9e410236", 47 | "site": "segment-com", 48 | "type": "reward_web_hook", 49 | "test": true 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /sources/typeform/handler.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Typeform: Online form building and online surveys 3 | * Use case: When a form or survey is filled out, capture that information to send through to Segment to trigger other actions 4 | * 5 | * Please do not delete [used for Intellisense] 6 | * @param {ServerRequest} request The incoming webhook request 7 | * @param {Object.} settings Custom settings 8 | * @return {Promise} 9 | */ 10 | async function onRequest(request, settings) { 11 | let eventBody = request.json(); 12 | const formResponse = eventBody.form_response; 13 | 14 | // Iterates through nested fields to build question answer pairs 15 | for (var i = 0; i < formResponse.definition.fields.length; i++) { 16 | buildQuestion(formResponse.definition.fields[i], formResponse.form_id) 17 | buildAnswer(formResponse.answers[i], formResponse.definition.fields[i].id) 18 | } 19 | 20 | if (eventBody.event_type == 'form_response') { 21 | Segment.set({ 22 | collection: 'form_responses', 23 | id: formResponse.form_id, 24 | properties: { 25 | token: formResponse.token, 26 | submitTime: formResponse.submitted_at, 27 | landTime: formResponse.landed_at, 28 | formTitle: formResponse.definition.title 29 | } 30 | }) 31 | } 32 | } 33 | 34 | // Helper Functions 35 | 36 | function buildAnswerObj(fullAnswer) { 37 | if (fullAnswer["choices"] != undefined) { 38 | return fullAnswer["choices"]["labels"].join(); 39 | } else if (fullAnswer["choice"] != undefined) { 40 | return fullAnswer["choice"]["label"]; 41 | } else { 42 | return String(fullAnswer[fullAnswer.type]) 43 | } 44 | } 45 | 46 | function buildQuestion(formFields, id) { 47 | Segment.set({ 48 | collection: 'form_questions', 49 | id: id, 50 | properties: { 51 | responseId: id, 52 | title: formFields.title, 53 | type: formFields.type, 54 | ref: formFields.ref, 55 | allowMultipleSelections: formFields.allow_multiple_selections, 56 | allowOtherChoices: formFields.allow_other_choices 57 | } 58 | }) 59 | } 60 | 61 | function buildAnswer(answerFields, questionId) { 62 | Segment.set({ 63 | collection: 'form_answers', 64 | id: answerFields.field.id, 65 | properties: { 66 | questionId: questionId, 67 | type: answerFields.type, 68 | answer: buildAnswerObj(answerFields) 69 | } 70 | }) 71 | } 72 | -------------------------------------------------------------------------------- /sources/zoominfo/handler.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Handle incoming HTTP request 3 | * 4 | * @param {FunctionRequest} request 5 | * @param {FunctionSettings} settings 6 | */ 7 | async function onRequest(request, settings) { 8 | const body = request.json(); 9 | 10 | //handle ZoomInfo authentication 11 | const authResponse = await fetch('https://api.zoominfo.com/authenticate', { 12 | method: 'POST', 13 | headers: { 'Content-Type': 'application/json' }, 14 | body: JSON.stringify({ 15 | username: settings.aZoomInfoApiUsername, 16 | password: settings.bZoomInfoApiPassword 17 | }) 18 | }); 19 | let authJSON = await authResponse.json(); 20 | let JWT = authJSON['jwt']; 21 | 22 | //ZoomInfo API Docs: https://documenter.getpostman.com/view/7012197/SVYwLGXM?version=latest#cf83bb37-31cf-45d3-a276-08625ca04c60 23 | let callType = ''; 24 | let trackedProps = {}; 25 | let groupProps = {}; 26 | let zoomReq = { matchPersonInput: [], outputFields: [] }; 27 | let matchKeys = {}; 28 | let outputKeys = [ 29 | 'id', 30 | 'firstname', 31 | 'middlename', 32 | 'lastname', 33 | 'email', 34 | 'hascanadianemail', 35 | 'phone', 36 | 'directphonedonotcall', 37 | 'gender', 38 | 'street', 39 | 'city', 40 | 'region', 41 | 'metroarea', 42 | 'zipcode', 43 | 'state', 44 | 'country', 45 | 'personhasmoved', 46 | 'withineu', 47 | 'withincalifornia', 48 | 'withincanada', 49 | 'lastupdateddate', 50 | 'noticeprovideddate', 51 | 'salutation', 52 | 'suffix', 53 | 'jobtitle', 54 | 'jobfunction', 55 | 'companydivision', 56 | 'education', 57 | 'hashedemails', 58 | 'picture', 59 | 'mobilephonedonotcall', 60 | 'externalurls', 61 | 'companyid', 62 | 'companyname', 63 | 'companyphone', 64 | 'companyfax', 65 | 'companystreet', 66 | 'companycity', 67 | 'companystate', 68 | 'companyzipcode', 69 | 'companycountry', 70 | 'companylogo', 71 | 'contactaccuracyscore', 72 | 'companywebsite', 73 | 'companyrevenue', 74 | 'companyrevenuenumeric', 75 | 'companyemployeecount', 76 | 'companytype', 77 | 'companyticker', 78 | 'companyranking', 79 | 'isdefunct', 80 | 'companysocialmediaurls', 81 | 'companyprimaryindustry', 82 | 'companyindustries', 83 | 'companyrevenuerange', 84 | 'companyemployeerange', 85 | 'employmenthistory', 86 | 'managementlevel', 87 | 'companyId' 88 | ]; 89 | if (settings.cCustomOutputFields.length) { 90 | outputKeys = settings.cCustomOutputFields; 91 | } 92 | if ('type' in body) { 93 | callType = body['type']; 94 | } 95 | console.log(callType); 96 | if (callType == 'identify') { 97 | trackedProps = body['traits']; 98 | } else if (callType == 'track') { 99 | trackedProps = body['properties']; 100 | } 101 | 102 | let hasEmail, hasFirstName, hasLastName, hasFullName, hasCompany; 103 | if ('email' in trackedProps) { 104 | matchKeys['emailAddress'] = trackedProps['email']; 105 | hasEmail = true; 106 | } 107 | if ('first_name' in trackedProps) { 108 | matchKeys['firstName'] = trackedProps['first_name']; 109 | hasFirstName = true; 110 | } 111 | if ('last_name' in trackedProps) { 112 | matchKeys['lastName'] = trackedProps['last_name']; 113 | hasLastName = true; 114 | } 115 | if ('company' in trackedProps) { 116 | matchKeys['companyName'] = trackedProps['company']; 117 | hasCompany = true; 118 | } 119 | if ('name' in trackedProps) { 120 | matchKeys['fullName'] = trackedProps['name']; 121 | hasfullName = true; 122 | } 123 | 124 | if ( 125 | !hasEmail && 126 | !(hasFirstName && hasLastName && hasCompany) && 127 | !(hasFullName && hasCompany) && 128 | !settings.eAllowBadRequests 129 | ) { 130 | throw new Error('Insufficient match keys - abandoning request.'); 131 | return; 132 | } 133 | 134 | zoomReq['matchPersonInput'].push(matchKeys); 135 | zoomReq['outputFields'] = outputKeys; 136 | 137 | //query ZoomInfo 138 | const enrichURL = "https://api.zoominfo.com/enrich/contact" 139 | const enrichResponse = await fetch(enrichURL, { 140 | method: 'POST', 141 | headers: { 'Content-Type': 'application/json' }, 142 | body: JSON.stringify(zoomReq) 143 | }); 144 | let enrichJSON = await enrichResponse.json(); 145 | 146 | enrichmentProperties = enrichJSON['data']['result'][0]['data'][0]; 147 | console.log(typeof enrichmentProperties); 148 | 149 | let enrichedTraits = {}; 150 | //union traits if config desires it 151 | if (settings.bMaintainOriginalTraits == true) { 152 | enrichedTraits = { ...enrichmentProperties, ...body.traits }; 153 | } else { 154 | enrichedTraits = enrichmentProperties; 155 | } 156 | 157 | Segment.identify({ 158 | userId: body.userId || null, 159 | anonymousId: body.anonymousId || null, 160 | traits: enrichedTraits 161 | }); 162 | } 163 | -------------------------------------------------------------------------------- /sources/zoominfo/readme.md: -------------------------------------------------------------------------------- 1 | 2 | Integrate with ZoomInfo's "Contact Enrich" API. 3 | 4 | Recommended implementation: Create this as a Custom Source. For existing sources with users that you'd like to enrich with ZoomInfo data, connect them to a Webhook which sends to the URL of your Custom Source. You can use Destination Filters in front of the webhook if you'd only like to enrich a subset of your data. 5 | This source will check for applicable match keys (e.g., email, company, first/last name) and attempt to collect an enriched profile from the ZoomInfo Enterprise Contact Enrich API. Any new data will be tracked as an Identify call, which you can use as needed in Personas or any Segment Destination. 6 | 7 | Recommended Config Settings (already included in handler.js): 8 | --------------------------------------------------------------------------------