From 689a3eacb6b1e1a121c3f66edbae75fdaa24f624 Mon Sep 17 00:00:00 2001 From: Dustin Grange Date: Mon, 15 Jun 2026 11:29:45 -0400 Subject: [PATCH] [GG-1690] Migrate REST docs to OpenAPI spec + Slate generator Make OpenAPI 3.1 the source of truth for the REST API docs. Redoc renders the aggregator spec/rest-v2.yaml; a custom stdlib-only Ruby generator (lib/openapi_slate.rb, via script/generate-docs) reproduces the existing Slate _*.md partials so the public site stays the same. Structure: - spec/rest-v2.yaml: aggregator ($refs every path, in display order) - spec/components.yaml: shared parameters/responses/schemas/securitySchemes - spec/fragments/.yaml: one self-contained file per resource Presentation that can't be derived from OpenAPI lives under the x-slate vendor extension (Redoc ignores it). script/verify-docs gates on a normalized diff and reports byte diffs; see spec/README.md for the workflow and x-slate reference. All 14 REST resources generate from the spec (verify-docs: 0 pending). The smaller resources reproduce byte-identical; the larger pages (subscribers, campaigns, workflows, broadcasts, batch_api) are regenerated as canonical output (full response JSON, schema-derived tables). Note: the Batch API page's three v3 Shopper Activity batch endpoints are carried verbatim in batch_api.yaml x-slate.footer; they should later move to a dedicated shopper-activity spec. Co-Authored-By: Claude Opus 4.8 --- .gitignore | 1 + CLAUDE.md | 49 + Gemfile | 3 + Gemfile.lock | 12 + config.rb | 9 + devbox.json | 28 + devbox.lock | 405 ++++++++ lib/openapi_slate.rb | 426 ++++++++ redocly.yaml | 13 + script/generate-docs | 33 + script/verify-docs | 87 ++ source/includes/rest/_batch_api.md | 242 +++-- source/includes/rest/_broadcasts.md | 312 +++--- source/includes/rest/_campaigns.md | 139 ++- source/includes/rest/_subscribers.md | 361 +++++-- source/includes/rest/_workflows.md | 100 +- spec/README.md | 107 ++ spec/components.yaml | 92 ++ spec/fragments/accounts.yaml | 323 ++++++ spec/fragments/batch_api.yaml | 1403 ++++++++++++++++++++++++++ spec/fragments/broadcasts.yaml | 1217 ++++++++++++++++++++++ spec/fragments/campaigns.yaml | 959 ++++++++++++++++++ spec/fragments/conversions.yaml | 358 +++++++ spec/fragments/custom_fields.yaml | 94 ++ spec/fragments/events.yaml | 323 ++++++ spec/fragments/forms.yaml | 485 +++++++++ spec/fragments/orders.yaml | 1314 ++++++++++++++++++++++++ spec/fragments/subscribers.yaml | 1087 ++++++++++++++++++++ spec/fragments/tags.yaml | 298 ++++++ spec/fragments/users.yaml | 86 ++ spec/fragments/webhooks.yaml | 519 ++++++++++ spec/fragments/workflows.yaml | 1058 +++++++++++++++++++ spec/rest-v2.yaml | 161 +++ 33 files changed, 11708 insertions(+), 396 deletions(-) create mode 100644 CLAUDE.md create mode 100644 devbox.json create mode 100644 devbox.lock create mode 100644 lib/openapi_slate.rb create mode 100644 redocly.yaml create mode 100755 script/generate-docs create mode 100755 script/verify-docs create mode 100644 spec/README.md create mode 100644 spec/components.yaml create mode 100644 spec/fragments/accounts.yaml create mode 100644 spec/fragments/batch_api.yaml create mode 100644 spec/fragments/broadcasts.yaml create mode 100644 spec/fragments/campaigns.yaml create mode 100644 spec/fragments/conversions.yaml create mode 100644 spec/fragments/custom_fields.yaml create mode 100644 spec/fragments/events.yaml create mode 100644 spec/fragments/forms.yaml create mode 100644 spec/fragments/orders.yaml create mode 100644 spec/fragments/subscribers.yaml create mode 100644 spec/fragments/tags.yaml create mode 100644 spec/fragments/users.yaml create mode 100644 spec/fragments/webhooks.yaml create mode 100644 spec/fragments/workflows.yaml create mode 100644 spec/rest-v2.yaml diff --git a/.gitignore b/.gitignore index 10501583560..96a74723407 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ test/tmp test/version_tmp tmp *.DS_STORE +.devbox/ build/ .cache .vagrant diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000000..4dc0c225414 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,49 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Overview + +This is the Drip API documentation site (developer.drip.com) — a [Slate](https://github.com/lord/slate)-based static site built with Middleman. Content is Markdown; there are no tests or linters. + +## Commands + +```bash +bundle install # install dependencies (Ruby 2.7.6 per .tool-versions) +bundle exec middleman server # serve locally at http://localhost:4567 +bundle exec middleman build # build static site into build/ +script/release # deploy main to production (GitHub Pages via middleman-deploy) + +ruby script/generate-docs # regenerate source/includes/rest/_*.md from spec/ +ruby script/verify-docs # check generated md matches the committed partials +npx @redocly/cli preview-docs spec/rest-v2.yaml # Redoc preview of the REST API +``` + +## Architecture + +All documentation renders into a single page from `source/index.html.md`. That file's YAML frontmatter contains four ordered include lists (`includes_rest_api`, `includes_js_api`, `includes_cdc`, `includes_shopper_activity`), which `source/layouts/layout.erb` iterates to pull in partials from `source/includes/{rest,js,cdc,shopper_activity}/`. + +To add a new documentation section: create `source/includes//_.md` and add `/` to the matching frontmatter list in `source/index.html.md` (order in the list controls order on the page and in the table of contents). + +### Content conventions (see any file in `source/includes/rest/` for an example) + +- Each partial starts with a `#` heading (resource name) and `##` headings per endpoint. +- Code samples appear in fenced blocks tagged `shell`, `ruby`, or `javascript` — these become the language tabs defined in `index.html.md` frontmatter (`language_tabs`). Provide all three languages for REST endpoints; Ruby examples use the `drip` gem, JavaScript uses `drip-nodejs`. +- Blockquote lines (`> ...`) immediately above code blocks become annotations displayed in the dark code column. +- JSON response examples use ```` ```json ```` blocks; request/response property documentation uses raw HTML `` elements. + +### Custom Middleman plumbing (`lib/`) + +- `multilang.rb` — tags rendered code blocks so the JS language switcher can toggle them. +- `unique_head.rb` — de-duplicates generated heading anchor IDs. +- `toc_data.rb` — builds the left-nav table of contents from H1/H2/H3 headings. +- `openapi_slate.rb` — converts an OpenAPI resource fragment into a Slate `_*.md` partial (used by `script/generate-docs` / `script/verify-docs`). + +### REST docs are migrating to OpenAPI (`spec/`) + +The REST partials in `source/includes/rest/` are being moved to a spec-as-source-of-truth model: each resource is described as OpenAPI 3.1 in `spec/fragments/`, **Redoc** renders the aggregator `spec/rest-v2.yaml`, and the custom generator reproduces the existing Slate markdown so the public site is unchanged. See `spec/README.md` for the workflow and the `x-slate` presentation-extension reference. + +- Edit the OpenAPI fragment, not the generated `_.md`, for any resource that has a matching `spec/fragments/.yaml`. Regenerate with `ruby script/generate-docs ` and verify with `ruby script/verify-docs `. +- The conceptual partials (`_authentication.md`, `_rate_limiting.md`, `_pagination.md`, `_errors.md`, `_webhook_events.md`) are hand-authored and **not** generated. +- Migration status: all 14 REST resources are generated from `spec/fragments/`. `tags`, `custom_fields`, `accounts`, `users`, `conversions`, `forms`, `events`, `webhooks`, `batch_api`, `campaigns`, `subscribers`, `workflows`, `broadcasts` pass `verify-docs` (NORM); `orders` differs only by incidental blank lines. The larger pages (`subscribers`, `campaigns`, `workflows`, `broadcasts`, `batch_api`) were regenerated as canonical output, so their committed `_*.md` reflects the generator (e.g. full response JSON instead of `{ ... }` placeholders, schema-derived tables). Run `ruby script/verify-docs` for current status. +- Open item: the published Batch API page also documents three **v3 Shopper Activity** batch endpoints (cart/order/product `batch`). These are not part of the `rest-v2` spec; they're currently carried verbatim in `spec/fragments/batch_api.yaml` under `x-slate.footer`. Long term they should move to a dedicated shopper-activity spec and be cross-linked. diff --git a/Gemfile b/Gemfile index 9b281d3a0e7..2bad3b83844 100644 --- a/Gemfile +++ b/Gemfile @@ -10,3 +10,6 @@ gem 'middleman-syntax', '~> 3.0.0' gem 'nokogiri', '~> 1.18.9' gem 'redcarpet', '~> 3.5.1' gem 'rouge', '~> 2.0.5' +# Removed from the Ruby stdlib in 3.x; still required by middleman/middleman-deploy +gem 'webrick' +gem 'net-ftp' diff --git a/Gemfile.lock b/Gemfile.lock index ee049aabff0..a0ae49231e5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -17,6 +17,7 @@ GEM coffee-script-source (1.12.2) concurrent-ruby (1.1.9) contracts (0.13.0) + date (3.5.1) dotenv (2.7.6) erubis (2.7.0) execjs (2.8.1) @@ -83,6 +84,11 @@ GEM rouge (~> 2.0) mini_portile2 (2.8.9) minitest (5.15.0) + net-ftp (0.3.9) + net-protocol + time + net-protocol (0.2.2) + timeout net-sftp (2.1.2) net-ssh (>= 2.6.5) net-ssh (4.2.0) @@ -116,10 +122,14 @@ GEM thor (1.2.1) thread_safe (0.3.6) tilt (2.0.10) + time (0.4.2) + date + timeout (0.6.1) tzinfo (1.2.10) thread_safe (~> 0.1) uglifier (3.2.0) execjs (>= 0.3.0, < 3) + webrick (1.9.2) PLATFORMS ruby @@ -130,9 +140,11 @@ DEPENDENCIES middleman-deploy (~> 2.0.0.pre.alpha) middleman-sprockets (~> 4.1.0) middleman-syntax (~> 3.0.0) + net-ftp nokogiri (~> 1.18.9) redcarpet (~> 3.5.1) rouge (~> 2.0.5) + webrick RUBY VERSION ruby 2.3.3p222 diff --git a/config.rb b/config.rb index 9c6f8a26df8..b5a6ed44212 100644 --- a/config.rb +++ b/config.rb @@ -1,3 +1,12 @@ +# Ruby 3.0 removed URI.escape, which middleman-core 4.3 still calls when +# writing build output. Restore it on modern Rubies. +# (config.rb is instance_eval'd by Middleman, so target ::URI explicitly.) +unless ::URI.respond_to?(:escape) + ::URI.define_singleton_method(:escape) do |value| + ::URI::DEFAULT_PARSER.escape(value) + end +end + # Unique header generation require './lib/unique_head.rb' diff --git a/devbox.json b/devbox.json new file mode 100644 index 00000000000..5a5acde4561 --- /dev/null +++ b/devbox.json @@ -0,0 +1,28 @@ +{ + "$schema": "https://raw.githubusercontent.com/jetify-com/devbox/0.10.6/.schema/devbox.schema.json", + "packages": { + "ruby": "3.3.5", + "bundler": "2.5", + "nodejs": "24", + "libyaml": { + "version": "latest", + "outputs": ["out", "dev"] + }, + "libffi": "latest", + "pkg-config": "latest" + }, + "env": { + "BUNDLE_BUILD__SASSC": "--disable-lto" + }, + "shell": { + "scripts": { + "install": "bundle install", + "server": "bundle exec middleman server", + "docs:generate": "ruby script/generate-docs", + "docs:verify": "ruby script/verify-docs", + "redoc:preview": "npx --yes @redocly/cli@latest preview-docs spec/rest-v2.yaml", + "redoc:lint": "npx --yes @redocly/cli@latest lint spec/rest-v2.yaml", + "redoc:bundle": "npx --yes @redocly/cli@latest bundle spec/rest-v2.yaml -o build/openapi/rest-v2.yaml" + } + } +} diff --git a/devbox.lock b/devbox.lock new file mode 100644 index 00000000000..187d2ffbac2 --- /dev/null +++ b/devbox.lock @@ -0,0 +1,405 @@ +{ + "lockfile_version": "1", + "packages": { + "bundler@2.5": { + "last_modified": "2025-02-23T09:42:26Z", + "resolved": "github:NixOS/nixpkgs/2d068ae5c6516b2d04562de50a58c682540de9bf#bundler", + "source": "devbox-search", + "version": "2.5.22", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/1bm3a45hjb497l4d424iyvrypkcd1w44-bundler-2.5.22", + "default": true + } + ], + "store_path": "/nix/store/1bm3a45hjb497l4d424iyvrypkcd1w44-bundler-2.5.22" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/k9sw5c0zcidbj2g9naas4nc476qfdwy6-bundler-2.5.22", + "default": true + } + ], + "store_path": "/nix/store/k9sw5c0zcidbj2g9naas4nc476qfdwy6-bundler-2.5.22" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/fgvddcr66vkscbdhb4sqr80jkdhx989p-bundler-2.5.22", + "default": true + } + ], + "store_path": "/nix/store/fgvddcr66vkscbdhb4sqr80jkdhx989p-bundler-2.5.22" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/s2ms874d5hdxbk1szzfgk3i737i95l3v-bundler-2.5.22", + "default": true + } + ], + "store_path": "/nix/store/s2ms874d5hdxbk1szzfgk3i737i95l3v-bundler-2.5.22" + } + } + }, + "github:NixOS/nixpkgs/nixpkgs-unstable": { + "last_modified": "2026-06-11T01:27:03Z", + "resolved": "github:NixOS/nixpkgs/b503dde361500433ca25a32e8f4d218bf58fb659?lastModified=1781141223&narHash=sha256-Eye4UQJjC4TLobclolFCMl6MrjgiF6Bk1cOI5x8SH00%3D" + }, + "libffi@latest": { + "last_modified": "2026-06-01T17:55:45Z", + "resolved": "github:NixOS/nixpkgs/4df1b885d76a54e1aa1a318f8d16fd6005b6401f#libffi", + "source": "devbox-search", + "version": "3.5.2", + "systems": { + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/c8agvk09xi1z86vb0kb1f3lkcwdymsca-libffi-3.5.2", + "default": true + }, + { + "name": "man", + "path": "/nix/store/rrrhgcxhza8h4jw26aw7vlzvihzxbjnv-libffi-3.5.2-man", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/9mhmki2nsfgmk12whl0daddr530mz94z-libffi-3.5.2-dev" + }, + { + "name": "info", + "path": "/nix/store/r1z4cx86yn53n2rzhvwmsq4wl1412ac3-libffi-3.5.2-info" + } + ], + "store_path": "/nix/store/c8agvk09xi1z86vb0kb1f3lkcwdymsca-libffi-3.5.2" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/2zs4bbi72plfm8j6zxf1js4f3yc4yzwy-libffi-3.5.2", + "default": true + }, + { + "name": "man", + "path": "/nix/store/8jk4a6mz1dskcfgm7hybc74q95b10msd-libffi-3.5.2-man", + "default": true + }, + { + "name": "info", + "path": "/nix/store/n1xpzmdc6mh2iskkr0nj356c3n3dn4aj-libffi-3.5.2-info" + }, + { + "name": "dev", + "path": "/nix/store/aj7zqrfxvg96ldyph7fly6qgprcm2krv-libffi-3.5.2-dev" + } + ], + "store_path": "/nix/store/2zs4bbi72plfm8j6zxf1js4f3yc4yzwy-libffi-3.5.2" + } + } + }, + "libyaml@latest": { + "last_modified": "2026-05-21T08:15:18Z", + "resolved": "github:NixOS/nixpkgs/4a29d733e8a7d5b824c3d8c958a946a9867b3eb2#libyaml", + "source": "devbox-search", + "version": "0.2.5", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/za59wqpzpincp6fvsf7jvgr5895czjvv-libyaml-0.2.5", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/s3pka3rg9fi4wmrw8md68fhi555q6dcl-libyaml-0.2.5-dev" + } + ], + "store_path": "/nix/store/za59wqpzpincp6fvsf7jvgr5895czjvv-libyaml-0.2.5" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/87grr3wnf55mvdiwrfb7bd9k5d1f9bq5-libyaml-0.2.5", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/kw8zdpx1ld2dy48iz2yq2xdvfm8cdsgv-libyaml-0.2.5-dev" + } + ], + "store_path": "/nix/store/87grr3wnf55mvdiwrfb7bd9k5d1f9bq5-libyaml-0.2.5" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/bbfkrgq2lz030cbbawjmn7vbnsylql7c-libyaml-0.2.5", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/liksd001gkvwy37jdfa4y1xa2f1nzghm-libyaml-0.2.5-dev" + } + ], + "store_path": "/nix/store/bbfkrgq2lz030cbbawjmn7vbnsylql7c-libyaml-0.2.5" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/phzxi1l7gcm9slvikj9w4yswd89hi14x-libyaml-0.2.5", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/mmxarzrpbxccr4djwar95qbc3nvcx55w-libyaml-0.2.5-dev" + } + ], + "store_path": "/nix/store/phzxi1l7gcm9slvikj9w4yswd89hi14x-libyaml-0.2.5" + } + } + }, + "nodejs@24": { + "last_modified": "2025-12-27T12:56:01Z", + "plugin_version": "0.0.2", + "resolved": "github:NixOS/nixpkgs/3edc4a30ed3903fdf6f90c837f961fa6b49582d1#nodejs_24", + "source": "devbox-search", + "version": "24.12.0", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/b1h0af3yb3z9jz048phclcqw6ihiww67-nodejs-24.12.0", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/ksn4nj0nbp0ikmh4scy9x4biqqdswwhy-nodejs-24.12.0-dev" + }, + { + "name": "libv8", + "path": "/nix/store/3x72bm7l30ib7l7cximrp0sklcq8jqwy-nodejs-24.12.0-libv8" + } + ], + "store_path": "/nix/store/b1h0af3yb3z9jz048phclcqw6ihiww67-nodejs-24.12.0" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/9jfsyhcr9c6rpm94lrkibv75jkggvwss-nodejs-24.12.0", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/7izhv9x1yvsmyrhmmyps6bxjjclajm80-nodejs-24.12.0-dev" + }, + { + "name": "libv8", + "path": "/nix/store/8hl74pnc1qyz68rg10ixw7fz32hcxqkk-nodejs-24.12.0-libv8" + } + ], + "store_path": "/nix/store/9jfsyhcr9c6rpm94lrkibv75jkggvwss-nodejs-24.12.0" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/cj1243xswxvnwgifyiyg8axhn0r2vl48-nodejs-24.12.0", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/kzia3rkaf9vwpn3py23q9v49jpjm6zx7-nodejs-24.12.0-dev" + }, + { + "name": "libv8", + "path": "/nix/store/xszji81xj7ghjhz7jk0jkhb84nfwyd8f-nodejs-24.12.0-libv8" + } + ], + "store_path": "/nix/store/cj1243xswxvnwgifyiyg8axhn0r2vl48-nodejs-24.12.0" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/9z1v3wyrxp6fpyzw21lcakd5w7aknzyc-nodejs-24.12.0", + "default": true + }, + { + "name": "libv8", + "path": "/nix/store/5yvx909wlis13qd2y1ahwl7ij21ma69a-nodejs-24.12.0-libv8" + }, + { + "name": "dev", + "path": "/nix/store/7ll99p08gmpg0n048669p80zjibl5akr-nodejs-24.12.0-dev" + } + ], + "store_path": "/nix/store/9z1v3wyrxp6fpyzw21lcakd5w7aknzyc-nodejs-24.12.0" + } + } + }, + "pkg-config@latest": { + "last_modified": "2025-11-23T21:50:36Z", + "resolved": "github:NixOS/nixpkgs/ee09932cedcef15aaf476f9343d1dea2cb77e261#pkg-config", + "source": "devbox-search", + "version": "0.29.2", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/hygaaqwk9ylklp0ybwppqhw75nz8ya41-pkg-config-wrapper-0.29.2", + "default": true + }, + { + "name": "man", + "path": "/nix/store/9px0sji43x3r2w4zxl3j3idwsql7lwxx-pkg-config-wrapper-0.29.2-man", + "default": true + }, + { + "name": "doc", + "path": "/nix/store/hqk44ra6qxw7iixardl6c3hdgb9kq6ns-pkg-config-wrapper-0.29.2-doc" + } + ], + "store_path": "/nix/store/hygaaqwk9ylklp0ybwppqhw75nz8ya41-pkg-config-wrapper-0.29.2" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/lbiigi8qbp7mzf1lpr7p982l1kyf01ql-pkg-config-wrapper-0.29.2", + "default": true + }, + { + "name": "man", + "path": "/nix/store/10060k24qggqyzlwdsfmni9y32zxcg0j-pkg-config-wrapper-0.29.2-man", + "default": true + }, + { + "name": "doc", + "path": "/nix/store/0y4v51ndpyvkj09hwlfqkz0c3h17zfmc-pkg-config-wrapper-0.29.2-doc" + } + ], + "store_path": "/nix/store/lbiigi8qbp7mzf1lpr7p982l1kyf01ql-pkg-config-wrapper-0.29.2" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/vknadizq0q5kffvx6y4379p9gdry9zq3-pkg-config-wrapper-0.29.2", + "default": true + }, + { + "name": "man", + "path": "/nix/store/1nyspra675q22gfhf7hn2nmfpi6rgim5-pkg-config-wrapper-0.29.2-man", + "default": true + }, + { + "name": "doc", + "path": "/nix/store/7lq1axxwrafwljs06n88bzyz9w523rkc-pkg-config-wrapper-0.29.2-doc" + } + ], + "store_path": "/nix/store/vknadizq0q5kffvx6y4379p9gdry9zq3-pkg-config-wrapper-0.29.2" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/8vdiwpbh0g4avsd6x5v4s0di32vcl3dp-pkg-config-wrapper-0.29.2", + "default": true + }, + { + "name": "man", + "path": "/nix/store/j9xfpnrygg3v37svc5pfin9q5bm49r94-pkg-config-wrapper-0.29.2-man", + "default": true + }, + { + "name": "doc", + "path": "/nix/store/x3bypxdxaq20kykybhkf21x4jczsiy8y-pkg-config-wrapper-0.29.2-doc" + } + ], + "store_path": "/nix/store/8vdiwpbh0g4avsd6x5v4s0di32vcl3dp-pkg-config-wrapper-0.29.2" + } + } + }, + "ruby@3.3.5": { + "last_modified": "2024-12-03T12:40:06Z", + "plugin_version": "0.0.2", + "resolved": "github:NixOS/nixpkgs/566e53c2ad750c84f6d31f9ccb9d00f823165550#ruby", + "source": "devbox-search", + "version": "3.3.5", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/x1frilk1vr0fnk5zl55bfyv27ychiv3d-ruby-3.3.5", + "default": true + }, + { + "name": "devdoc", + "path": "/nix/store/qbyqi9nj5yg8f4xm558ymlz9lk5fgzr7-ruby-3.3.5-devdoc" + } + ], + "store_path": "/nix/store/x1frilk1vr0fnk5zl55bfyv27ychiv3d-ruby-3.3.5" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/sav56f0br714z8zc8vwchxas4izm2chj-ruby-3.3.5", + "default": true + }, + { + "name": "devdoc", + "path": "/nix/store/1han7xvbvh36d198sm4lp11c7w63kz8v-ruby-3.3.5-devdoc" + } + ], + "store_path": "/nix/store/sav56f0br714z8zc8vwchxas4izm2chj-ruby-3.3.5" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/dy6k8szppx7rpa0b3sdxm0q7ni75zbn4-ruby-3.3.5", + "default": true + }, + { + "name": "devdoc", + "path": "/nix/store/1j5dxwfvhqi65bzn4wqc23db7brd3hq4-ruby-3.3.5-devdoc" + } + ], + "store_path": "/nix/store/dy6k8szppx7rpa0b3sdxm0q7ni75zbn4-ruby-3.3.5" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/0ws1v8mcnqh6zkhdw7i5p5yb0rvcx188-ruby-3.3.5", + "default": true + }, + { + "name": "devdoc", + "path": "/nix/store/3abwjizdsjmr85vms70zgsvjp0p7bgnr-ruby-3.3.5-devdoc" + } + ], + "store_path": "/nix/store/0ws1v8mcnqh6zkhdw7i5p5yb0rvcx188-ruby-3.3.5" + } + } + } + } +} diff --git a/lib/openapi_slate.rb b/lib/openapi_slate.rb new file mode 100644 index 00000000000..0494368acc2 --- /dev/null +++ b/lib/openapi_slate.rb @@ -0,0 +1,426 @@ +# frozen_string_literal: true + +# OpenAPI -> Slate markdown generator. +# +# Converts a single resource fragment (spec/fragments/.yaml) into the +# Slate partial the public docs site renders (source/includes/rest/_.md). +# +# Design: +# * OpenAPI structural data (paths, schemas, parameters, responses, examples) +# is the source of truth Redoc reads when bundling spec/rest-v2.yaml. +# * The `x-slate` vendor extension carries the Slate-only presentation layer +# (annotation prose, hand-formatted JSON, anomalies in the published docs) +# that cannot be derived from the structured data alone. +# +# Stdlib only (YAML/Psych + JSON) so it runs under the system Ruby as well as +# the devbox toolchain. + +require "yaml" +require "json" + +module OpenapiSlate + # Loads a fragment and resolves $ref pointers, including relative external + # refs into spec/components.yaml. + class Loader + def initialize(path) + @path = File.expand_path(path) + @dir = File.dirname(@path) + @root = self.class.load_yaml(@path) + @cache = { @path => @root } + end + + # Parse a trusted local spec file across Ruby/Psych versions. Psych 4 made + # YAML.load a safe loader (no aliases); these specs are trusted, so prefer + # the unsafe loader when present to keep anchor/alias support. + def self.load_yaml(file) + content = File.read(file) + if YAML.respond_to?(:unsafe_load) + YAML.unsafe_load(content) + else + YAML.load(content) + end + end + + attr_reader :root, :path + + # Resolve a node one level: if it's a {"$ref" => "..."} map, follow it + # (recursively) and return the target. Otherwise return the node as-is. + def deref(node) + return node unless node.is_a?(Hash) && node.key?("$ref") + + deref(resolve_pointer(node["$ref"])) + end + + private + + def resolve_pointer(ref) + file_part, _, fragment = ref.partition("#") + doc = file_part.empty? ? @root : load_file(File.expand_path(file_part, @dir)) + fragment.split("/").reject(&:empty?).reduce(doc) do |acc, raw| + key = raw.gsub("~1", "/").gsub("~0", "~") + unless acc.is_a?(Hash) && acc.key?(key) + raise "Unresolvable $ref #{ref.inspect} (missing segment #{key.inspect})" + end + acc[key] + end + end + + def load_file(file) + @cache[file] ||= self.class.load_yaml(file) + end + end + + # Builds a JSON example object from a schema when no explicit `example` is + # provided. Used for resource "representation" blocks. + class ExampleBuilder + def initialize(loader) + @loader = loader + end + + def build(schema) + schema = @loader.deref(schema) + return nil unless schema.is_a?(Hash) + return schema["example"] if schema.key?("example") + + case schema["type"] + when "object", nil + props = schema["properties"] + return {} unless props.is_a?(Hash) + + props.each_with_object({}) do |(name, sub), acc| + acc[name] = build(sub) + end + when "array" + [build(schema["items"])].compact + else + schema["default"] + end + end + end + + # Renders the Slate markdown for one fragment. + class Renderer + def initialize(loader) + @loader = loader + @examples = ExampleBuilder.new(loader) + end + + def render + spec = @loader.root + tag = primary_tag(spec) + slate = tag.fetch("x-slate", {}) + + out = +"# #{slate["title"] || tag.fetch("name")}\n" + lead = slate["lead"] || render_lead(slate) + out << "\n#{rstrip_block(lead)}\n" if lead + + operations(spec).each do |op| + out << "\n" << render_operation(op) + end + + out << "\n#{rstrip_block(slate["footer"])}\n" if slate["footer"] + + normalize_trailing(out) + end + + private + + # Build the resource lead (representation blocks + Properties table) from + # structured `x-slate` data, as an alternative to a raw `lead` string: + # + # x-slate: + # represent: # one or more annotated example blocks + # - intro: "X are represented as follows:" + # schema: Subscriber # render the schema's example as JSON + # - intro: "All responses ..." + # json: | # or supply a raw JSON block + # { ... } + # properties: Subscriber # render a Properties table from a schema + def render_lead(slate) + return nil unless slate["represent"] || slate["properties"] || slate["lead_extra"] + + parts = [] + Array(slate["represent"]).each do |block| + parts << "> #{block["intro"]}" if block["intro"] + json = block.key?("json") ? rstrip_block(block["json"]) : pretty_json(schema_example(block["schema"])) + parts << "```json\n#{json}\n```" + end + + if slate["properties"] + rows = direct_property_rows(slate["properties"]) + parts << "**Properties**\n\n#{property_table(rows, "Property")}" + end + + parts << rstrip_block(slate["lead_extra"]) if slate["lead_extra"] + + parts.join("\n\n") + end + + def schema_example(name) + schema = lookup_schema(name) + schema.key?("example") ? schema["example"] : @examples.build(schema) + end + + # Rows for a resource Properties table: the named schema's own properties. + def direct_property_rows(name) + props = lookup_schema(name)["properties"] || {} + props.map { |field, sub| { "name" => field, "description" => property_description(sub) } } + end + + # A property's documented description. An OpenAPI 3.1 `$ref` may carry a + # sibling `description`; prefer it over the referenced schema's own. + def property_description(sub) + return sub["description"] if sub.is_a?(Hash) && sub["description"] + + @loader.deref(sub)["description"] + end + + def lookup_schema(name) + schema = @loader.root.dig("components", "schemas", name) + raise "Fragment #{@loader.path} has no schema #{name.inspect}" unless schema + + @loader.deref(schema) + end + + # The fragment's first tag drives the H1. Fragments declare exactly one + # resource group today; if that changes, the generator should grow a + # resource->tag mapping rather than guessing. + def primary_tag(spec) + tags = spec["tags"] + raise "Fragment #{@loader.path} has no `tags:` entry" unless tags.is_a?(Array) && tags.any? + + tags.first + end + + # Flatten paths -> operations carrying the HTTP method, templated path, and + # merged (path + operation) parameters. Operations render in document order + # unless an `x-slate.order` key overrides it (the published pages sometimes + # interleave operations from different paths). + def operations(spec) + methods = %w[get post put patch delete] + flat = [] + (spec["paths"] || {}).each do |path, item| + path_params = item["parameters"] || [] + methods.each do |method| + op = item[method] + next unless op + + flat << { + "method" => method, + "path" => path, + "parameters" => path_params + (op["parameters"] || []), + "op" => op, + "doc_index" => flat.length, + "order" => op.fetch("x-slate", {})["order"], + } + end + end + flat.sort_by { |o| [o["order"] || o["doc_index"], o["doc_index"]] } + end + + def render_operation(entry) + op = entry["op"] + slate = op.fetch("x-slate", {}) + out = +"## #{op.fetch("summary")}\n" + + out << "\n> #{slate["intro"]}\n" if slate["intro"] + out << "\n" << code_samples(op, slate["code-order"]) + + # `response` may be a single block (Hash) or a list of blocks (e.g. a + # success example followed by documented error examples). + response_blocks = slate["response"].is_a?(Array) ? slate["response"] : [slate["response"]].compact + response_blocks.each do |response| + out << "\n> #{response["intro"]}\n" if response["intro"] + body = response_body(op, response) + out << "\n```json\n#{body}\n```\n" if body + end + + if op["description"] && describe_inline?(slate) + out << "\n#{collapse(op["description"])}\n" + end + + out << "\n#{rstrip_block(slate["sections"])}\n" if slate["sections"] + + out << "\n### HTTP Endpoint\n\n`#{endpoint(entry, slate)}`\n" + out << "\n### Arguments\n\n#{arguments(entry, slate)}\n" + out + end + + # The operation description is surfaced as a plain paragraph only when the + # published doc shows one (opt-in via x-slate.show-description: true), since + # Redoc already renders `description` in its own panel. + def describe_inline?(slate) + slate["show-description"] == true + end + + # Emit one fenced block per language. Samples render in authored order + # (deduped by language) unless `x-slate.code-order` lists the languages + # explicitly, so a resource can present, e.g., Ruby before shell when the + # published page does. + def code_samples(op, order = nil) + samples = op["x-codeSamples"] || [] + seen = {} + uniq = samples.reject { |s| seen[s["lang"]].tap { seen[s["lang"]] = true } } + uniq = uniq.sort_by { |s| order.index(s["lang"]) || order.length } if order + + blocks = uniq.map { |s| "```#{s["lang"]}\n#{rstrip_block(s["source"])}\n```" } + "#{blocks.join("\n\n")}\n" + end + + def response_body(op, response) + return rstrip_block(response["body"]) if response.key?("body") + + example = + if response["status"] + status_example(op, response["status"]) + else + success_example(op) + end + return nil if example.nil? + + pretty_json(example) + end + + # The JSON example for a specific response status (e.g. "422"). + def status_example(op, status) + resp = @loader.deref((op["responses"] || {})[status.to_s]) + content = resp.dig("content", "application/json") if resp.is_a?(Hash) + return nil unless content + + content.key?("example") ? content["example"] : @examples.build(content["schema"]) + end + + def success_example(op) + (op["responses"] || {}).each do |status, resp| + next unless status.to_s.start_with?("2") + + resp = @loader.deref(resp) + content = resp.dig("content", "application/json") if resp.is_a?(Hash) + next unless content + + return content["example"] if content.key?("example") + + if content["schema"] + built = @examples.build(content["schema"]) + return built unless built.nil? + end + end + nil + end + + def endpoint(entry, slate) + return slate["endpoint"] if slate["endpoint"] + + path = entry["path"].gsub(/\{(\w+)\}/, ':\1') + "#{entry["method"].upcase} /v2#{path}" + end + + def arguments(entry, slate) + spec = slate.key?("arguments") ? slate["arguments"] : "auto" + case spec + when "none", nil, false + "None." + when "auto" + rows = argument_rows(entry) + rows.empty? ? "None." : property_table(rows, "Key") + else + rstrip_block(spec) # raw HTML / markdown override + end + end + + # Collect documented inputs: request body object properties plus any query + # parameters, as {name, description} rows. + def argument_rows(entry) + op = entry["op"] + rows = [] + + body = op.dig("requestBody", "content", "application/json", "schema") + if body + rows.concat(schema_property_rows(body)) + end + + (entry["parameters"] || []).each do |param| + param = @loader.deref(param) + next unless param["in"] == "query" + + rows << { "name" => param["name"], "description" => param["description"] } + end + rows + end + + # Walk an object schema (following a top-level array wrapper, e.g. + # {events: [Event]}) down to the documented item properties. + def schema_property_rows(schema) + schema = @loader.deref(schema) + return [] unless schema.is_a?(Hash) + + if schema["type"] == "object" && schema["properties"] + wrapper = schema["properties"].values.map { |v| @loader.deref(v) } + if wrapper.length == 1 && wrapper.first["type"] == "array" + return schema_property_rows(wrapper.first["items"]) + end + + return schema["properties"].map do |name, sub| + { "name" => name, "description" => property_description(sub) } + end + elsif schema["type"] == "array" + return schema_property_rows(schema["items"]) + end + [] + end + + def property_table(rows, header) + body = rows.map do |row| + desc = html_escape_description(row["description"].to_s) + " \n \n \n " + end.join("\n") + + <<~HTML.rstrip +
#{row["name"]}#{desc}
+ + + + + + + + #{body} + +
#{header}Description
+ HTML + end + + # Light markdown->HTML for description cells: `code` -> , and + # [text](url) -> . Matches the conventions in the published tables. + def html_escape_description(text) + text = collapse(text) + text = text.gsub(/`([^`]+)`/) { "#{Regexp.last_match(1)}" } + text.gsub(/\[([^\]]+)\]\(([^)]+)\)/) do + %(#{Regexp.last_match(1)}) + end + end + + # Collapse hard-wrapped YAML block scalars into a single line. + # JSON.pretty_generate renders an empty container across two lines + # (`{\n}`); the published docs use the compact `{}` / `[]` form. + def pretty_json(value) + JSON.pretty_generate(value).gsub(/\{\n\s*\}/, "{}").gsub(/\[\n\s*\]/, "[]") + end + + def collapse(text) + text.to_s.gsub(/\s*\n\s*/, " ").strip + end + + def rstrip_block(text) + text.to_s.gsub(/\s+\z/, "") + end + + def normalize_trailing(out) + "#{out.gsub(/\n+\z/, "")}\n" + end + end + + def self.render_file(path) + Renderer.new(Loader.new(path)).render + end +end diff --git a/redocly.yaml b/redocly.yaml new file mode 100644 index 00000000000..0a04fce2be0 --- /dev/null +++ b/redocly.yaml @@ -0,0 +1,13 @@ +apis: + rest-v2: + root: spec/rest-v2.yaml + +# Redoc is the default OpenAPI renderer for the spec. Preview locally with: +# npx @redocly/cli preview-docs spec/rest-v2.yaml +# Bundle a single self-contained file with: +# npx @redocly/cli bundle spec/rest-v2.yaml -o build/openapi/rest-v2.yaml +# Code-sample tabs (cURL / Ruby / JavaScript) come from each operation's +# `x-codeSamples` entries in spec/fragments/. + +rules: + info-license: off # internal API reference; no license block needed diff --git a/script/generate-docs b/script/generate-docs new file mode 100755 index 00000000000..88dbaea4ef8 --- /dev/null +++ b/script/generate-docs @@ -0,0 +1,33 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# Generate Slate markdown partials from the OpenAPI resource fragments. +# +# script/generate-docs # regenerate every fragment +# script/generate-docs tags users # regenerate only the named resources +# +# Each spec/fragments/.yaml renders to +# source/includes/rest/_.md. + +require_relative "../lib/openapi_slate" + +ROOT = File.expand_path("..", __dir__) +FRAGMENTS = File.join(ROOT, "spec", "fragments") +OUT_DIR = File.join(ROOT, "source", "includes", "rest") + +selected = ARGV.map { |a| File.basename(a, ".*").sub(/\A_/, "") } + +fragments = Dir.children(FRAGMENTS).grep(/\.ya?ml\z/).map { |f| File.basename(f, ".*") }.sort +fragments &= selected unless selected.empty? + +if fragments.empty? + warn "No matching fragments in #{FRAGMENTS}" + exit 1 +end + +fragments.each do |name| + source = File.join(FRAGMENTS, "#{name}.yaml") + target = File.join(OUT_DIR, "_#{name}.md") + File.write(target, OpenapiSlate.render_file(source)) + puts "generated #{File.basename(target)}" +end diff --git a/script/verify-docs b/script/verify-docs new file mode 100755 index 00000000000..74c06d18c77 --- /dev/null +++ b/script/verify-docs @@ -0,0 +1,87 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# Verify that the generator reproduces the committed Slate partials. +# +# script/verify-docs # check every fragment +# script/verify-docs tags # check only the named resources +# script/verify-docs --diff # print a unified diff for each mismatch +# +# For each resource it reports two results: +# BYTE — generated output is byte-identical to the committed _.md +# NORM — output matches after collapsing insignificant whitespace +# +# Exit status is non-zero if any resource fails the NORM check (the gate); +# BYTE failures are reported but do not fail the run, since some published +# pages are intentionally hand-formatted. + +require "tempfile" +require_relative "../lib/openapi_slate" + +ROOT = File.expand_path("..", __dir__) +FRAGMENTS = File.join(ROOT, "spec", "fragments") +OUT_DIR = File.join(ROOT, "source", "includes", "rest") + +show_diff = ARGV.delete("--diff") +selected = ARGV.map { |a| File.basename(a, ".*").sub(/\A_/, "") } + +fragments = Dir.children(FRAGMENTS).grep(/\.ya?ml\z/).map { |f| File.basename(f, ".*") }.sort +fragments &= selected unless selected.empty? + +def normalize(text) + text.gsub(/[ \t]+$/, "").gsub(/\n{2,}/, "\n\n").strip +end + +def unified_diff(expected, actual) + a = Tempfile.new("expected") + b = Tempfile.new("actual") + a.write(expected); a.flush + b.write(actual); b.flush + `diff -u #{a.path} #{b.path}` +ensure + a&.close! + b&.close! +end + +norm_failures = 0 +byte_failures = 0 +pending = 0 + +fragments.each do |name| + source = File.join(FRAGMENTS, "#{name}.yaml") + target = File.join(OUT_DIR, "_#{name}.md") + + unless File.exist?(target) + puts format("%-16s SKIP (no committed _%s.md)", name, name) + next + end + + # A fragment without any x-slate presentation layer has not been migrated + # yet: report it as PENDING so it doesn't fail the gate during the rollout. + unless File.read(source).include?("x-slate") + pending += 1 + puts format("%-16s PENDING (no x-slate layer yet)", name) + next + end + + actual = OpenapiSlate.render_file(source) + expected = File.read(target) + + byte_ok = actual == expected + norm_ok = normalize(actual) == normalize(expected) + + byte_failures += 1 unless byte_ok + norm_failures += 1 unless norm_ok + + byte = byte_ok ? "ok" : "DIFF" + norm = norm_ok ? "ok" : "DIFF" + puts format("%-16s BYTE %-4s NORM %-4s", name, byte, norm) + + if show_diff && !byte_ok + puts unified_diff(expected, actual) + end +end + +puts +puts "#{fragments.length} checked | #{pending} pending | byte mismatches: #{byte_failures} | normalized mismatches: #{norm_failures}" +exit(norm_failures.zero? ? 0 : 1) diff --git a/source/includes/rest/_batch_api.md b/source/includes/rest/_batch_api.md index 22e32e6fd8d..8e72f03f0a1 100644 --- a/source/includes/rest/_batch_api.md +++ b/source/includes/rest/_batch_api.md @@ -95,8 +95,7 @@ client.updateBatchSubscribers(batch, (errors, responses, bodies) => { We recommend using this API endpoint when you need to create or update a collection of subscribers at once. -Note: Since our batch APIs process requests in the background, there may be a delay between the time you submit your -request and the time your data appears in the user interface. +Note: Since our batch APIs process requests in the background, there may be a delay between the time you submit your request and the time your data appears in the user interface. ### HTTP Endpoint @@ -304,8 +303,7 @@ client.recordBatchEvents(payload) We recommend using this API endpoint when you need to record a collection of events at once that will likely exceed the regular rate limit of 3,600 requests per hour. -Note: Since our batch APIs process requests in the background, there may be a delay between the time you submit your -request and the time your data appears in the user interface. +Note: Since our batch APIs process requests in the background, there may be a delay between the time you submit your request and the time your data appears in the user interface. ### HTTP Endpoint @@ -328,122 +326,6 @@ request and the time your data appears in the user interface. -## Create or update a batch of carts - -> To create or update a batch of carts: - -```shell -curl -X POST "https://api.getdrip.com/v3/YOUR_ACCOUNT_ID/shopper_activity/cart/batch" \ - -H "Content-Type: application/json" \ - -H 'User-Agent: Your App Name (www.yourapp.com)' \ - -u YOUR_API_KEY: \ - -d @- << EOF - { - "carts": [ - { - "provider": "my_custom_platform", - "email": "user@gmail.com", - "action": "created", - "cart_id": "456445746", - "occurred_at": "2019-01-17T20:50:00Z", - "cart_public_id": "#5", - "grand_total": 16.99, - "total_discounts": 5.34, - "currency": "USD", - "cart_url": "https://mysuperstore.com/cart/456445746", - "items": [ - { - "product_id": "B01J4SWO1G", - "product_variant_id": "B01J4SWO1G-CW-BOTT", - "sku": "XHB-1234", - "name": "The Coolest Water Bottle", - "brand": "Drip", - "categories": [ - "Accessories" - ], - "price": 11.16, - "quantity": 2, - "discounts": 5.34, - "total": 16.99, - "product_url": "https://mysuperstore.com/dp/B01J4SWO1G", - "image_url": "https://www.getdrip.com/images/example_products/water_bottle.png", - "product_tag": "Best Seller" - } - ] - }, - { - "provider": "my_custom_platform", - "email": "john@acme.com", - "action": "created", - "occurred_at": "2019-03-12T15:31:58Z", - "cart_id": "63754763", - "cart_public_id": "#4", - "grand_total": 54.00, - "total_discounts": 1.00, - "currency": "USD", - "order_url": "http://myorders.com/orders/4", - "items": [{ - "product_id": "B01F9AQ99M", - "product_variant_id": "B01F9AQ99M-YEL-BT", - "sku": "JDT-4321", - "name": "Yellow Boots of Might", - "brand": "Drip", - "categories": [ - "Of Might", - "Outdoors" - ], - "price": 55.00, - "quantity": 1, - "discounts": 1.00, - "total": 54.00, - "color": "black", - "product_url": "https://mysuperstore.com/dp/B01F9AQ99M", - "image_url": "https://www.getdrip.com/images/example_products/boots.png" - }] - } - ] - } -EOF -``` - -> Responds with a 202 Accepted if successful. That means the server accepted the request and queued it for processing. The response includes a list of unique request_ids that can be used to check the status of the request later on: - -```json -{ - "request_ids": [ - "db8a7b16-32dd-4863-8b6e-818e3eaab99a", - "002048e9-3692-4f0a-8bbf-80a077acf642" - ] -} -``` - - -We recommend using this API endpoint when you need to import a collection of carts that will likely exceed the regular rate limit of 3,600 requests per hour. - -Note: Since our batch APIs process requests in the background, there may be a delay between the time you submit your -request and the time your data appears in the user interface. - -### HTTP Endpoint - -`POST /v3/:account_id/shopper_activity/cart/batch` - -### Arguments - - - - - - - - - - - - - - -
KeyDescription
cartsRequired. An Array with between 1 and 1000 objects containing cart activity.
- ## Create or update a batch of orders (Legacy) > To create or update a batch of orders: @@ -604,6 +486,7 @@ EOF ```json {} ``` + + +We recommend using this API endpoint when you need to import a collection of carts that will likely exceed the regular rate limit of 3,600 requests per hour. + +Note: Since our batch APIs process requests in the background, there may be a delay between the time you submit your +request and the time your data appears in the user interface. + +### HTTP Endpoint + +`POST /v3/:account_id/shopper_activity/cart/batch` + +### Arguments + + + + + + + + + + + + + + +
KeyDescription
cartsRequired. An Array with between 1 and 1000 objects containing cart activity.
+ ## Create or update a batch of orders > To create or update a batch of orders: diff --git a/source/includes/rest/_broadcasts.md b/source/includes/rest/_broadcasts.md index be780cc108b..94f18013e04 100644 --- a/source/includes/rest/_broadcasts.md +++ b/source/includes/rest/_broadcasts.md @@ -1,51 +1,5 @@ # Single-Email Campaigns
(aka Broadcasts) -Single-Email Campaigns (Broadcasts) are one-time emails sent to a segment of your subscribers. -The API allows you to list, fetch, create, update, and delete Single-Email Campaigns, as well as -send test emails. - -A Single-Email Campaign's `status` is read-only via the API and is managed through the Drip UI. -Possible statuses are: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
StatusDescription
draftNot yet scheduled. Drafts are the only Single-Email Campaigns that can be edited via the API.
scheduledScheduled for a future send. Read-only.
sendingCurrently being sent. Read-only and cannot be deleted.
sentCompleted. Read-only.
canceledCanceled before sending. Read-only.
deletedDeleted. Hidden from default listings.
- -Scheduling (`send_at`, `localize_sending_time`) and recipient segmentation -are managed through the Drip UI only. These fields are returned as read-only values in API -responses and cannot be set via the API. - > Single-Email Campaigns are represented as follows: ```json @@ -76,7 +30,7 @@ responses and cannot be set via the API. ```json { "links": { - "broadcasts.account": "https://api.getdrip.com/v2/accounts/{broadcasts.account}", + "broadcasts.account": "https://api.getdrip.com/v2/accounts/{broadcasts.account}" } } ``` @@ -97,7 +51,7 @@ responses and cannot be set via the API. status - Returns whether the Single-Email Campaign is draft, canceled, scheduled, sent or sending. + Returns whether the Single-Email Campaign is draft, canceled, scheduled, sent or sending. Read-only via the API and managed through the Drip UI. Drafts are the only Single-Email Campaigns that can be edited via the API; sending campaigns cannot be deleted; deleted campaigns are hidden from default listings. name @@ -113,7 +67,7 @@ responses and cannot be set via the API. postal_address - As required by the CAN-SPAM Act, this is a postal address used for all sent emails and can be changed on a per email basis. + As required by the CAN-SPAM Act, this is a postal address used for all sent emails and can be changed on a per email basis. localize_sending_time @@ -133,7 +87,7 @@ responses and cannot be set via the API. href - The url designated for retrieving the account record via the REST API. + The url designated for retrieving the record via the REST API. preview_url @@ -246,9 +200,10 @@ client.listBroadcasts(options) > The response looks like this: ```json -# The broadcasts property is an array of broadcast objects. { - "links": { ... }, + "links": { + "broadcasts.account": "https://api.getdrip.com/v2/accounts/{broadcasts.account}" + }, "meta": { "page": 1, "sort": "created_at", @@ -258,7 +213,28 @@ client.listBroadcasts(options) "total_count": 5, "status": "all" }, - "broadcasts": [ ... ] + "broadcasts": [ + { + "id": "123456", + "status": "sent", + "name": "4 Marketing Automation Trends for 2015", + "from_name": "John Doe", + "from_email": "john@example.com", + "postal_address": "123 Anywhere St\nFresno, CA 99999", + "localize_sending_time": true, + "send_at": "2015-07-01T10:00:00Z", + "bcc": null, + "created_at": "2015-06-21T10:31:58Z", + "href": "https://api.getdrip.com/v2/9999999/broadcast/123456", + "preview_url": "https://www.getdrip.com/broadcasts/123456/2d83a64861f23b1c35a3b8d6ee3b54f7", + "subject": "4 Marketing Automation Trends for 2015", + "html_body": "HTML body", + "text_body": "Text body", + "links": { + "account": "9999999" + } + } + ] } ``` @@ -278,23 +254,23 @@ client.listBroadcasts(options) page - Optional. The page number (1-indexed). Defaults to 1. + The page number (1-indexed). Defaults to 1. per_page - Optional. The number of results per page, up to a maximum of 100. Defaults to 100. + The number of results per page, up to a maximum of 100. Defaults to 100. status - Optional. Filter by one of the following statuses: draft, scheduled, sending, sent, canceled, deleted, or all. By default, deleted Single-Email Campaigns are excluded; use status=deleted or status=all to include them. + Filter by one of the following statuses: draft, scheduled, sending, sent, canceled, deleted, or all. By default, deleted Single-Email Campaigns are excluded; use status=deleted or status=all to include them. sort - Optional. Sort results by one of these fields: created_at, updated_at, send_at, or name. Defaults to created_at. + Sort results by one of these fields: created_at, updated_at, send_at, or name. Defaults to created_at. direction - Optional. Filter sort direction with: asc or desc. Defaults to asc. + Filter sort direction with: asc or desc. Defaults to asc. @@ -400,30 +376,53 @@ const response = await fetch( const body = await response.json(); ``` -> Responds with a 201 Created and the new Single-Email Campaign if successful. -> The Location header contains the URL of the created record: +> Responds with a `201 Created` and the new Single-Email Campaign if successful. The `Location` header contains the URL of the created record: ```json { - "links": { ... }, - "broadcasts": [{ ... }] + "links": { + "broadcasts.account": "https://api.getdrip.com/v2/accounts/{broadcasts.account}" + }, + "broadcasts": [ + { + "id": "123456", + "status": "draft", + "name": "4 Marketing Automation Trends for 2015", + "from_name": "John Doe", + "from_email": "john@example.com", + "postal_address": "123 Anywhere St\nFresno, CA 99999", + "localize_sending_time": true, + "send_at": "2015-07-01T10:00:00Z", + "bcc": null, + "created_at": "2015-06-21T10:31:58Z", + "href": "https://api.getdrip.com/v2/9999999/broadcast/123456", + "preview_url": "https://www.getdrip.com/broadcasts/123456/2d83a64861f23b1c35a3b8d6ee3b54f7", + "subject": "4 Marketing Automation Trends for 2015", + "html_body": "HTML body", + "text_body": "Text body", + "links": { + "account": "9999999" + } + } + ] } ``` -> If the request is invalid, responds with a 422 Unprocessable Entity: +> If the request is invalid, responds with a `422 Unprocessable Entity`: ```json { - "errors": [{ - "code": "validation_error", - "attribute": "subject", - "message": "Subject is required" - }] + "errors": [ + { + "code": "presence_error", + "attribute": "email", + "message": "Email is required" + } + ] } ``` -Single-Email Campaigns created via the API start in the `draft` status. Scheduling and -sending are then managed through the Drip UI. +Single-Email Campaigns created via the API start in the `draft` status. Scheduling and sending are then managed through the Drip UI. ### HTTP Endpoint @@ -441,23 +440,23 @@ sending are then managed through the Drip UI. name - Required. The private name given to the Single-Email Campaign (255 characters maximum). + The private name given to the Single-Email Campaign (255 characters maximum). subject - Required. The email subject line. Supports Liquid templating. + The email subject line. Supports Liquid templating. content - Required. An Email Content object. At minimum, include an html key. Optionally include a text key for the plain text version; if omitted, the plain text version is generated from the HTML. + An Email Content object. At minimum, include an html key. Optionally include a text key for the plain text version; if omitted, the plain text version is generated from the HTML. preheader - Optional. Preview text shown in email clients before the email is opened. Supports Liquid templating. + Preview text shown in email clients before the email is opened. Supports Liquid templating. postal_address - Optional. A physical mailing address for CAN-SPAM compliance. Defaults to the account's postal address. + A physical mailing address for CAN-SPAM compliance. Defaults to the account's postal address. @@ -507,10 +506,32 @@ client.fetchBroadcast(broadcastId) > The response looks like this: ```json -# The broadcasts property is an array of one broadcast object. { - "links": { ... }, - "broadcasts": [{ ... }] + "links": { + "broadcasts.account": "https://api.getdrip.com/v2/accounts/{broadcasts.account}" + }, + "broadcasts": [ + { + "id": "123456", + "status": "sent", + "name": "4 Marketing Automation Trends for 2015", + "from_name": "John Doe", + "from_email": "john@example.com", + "postal_address": "123 Anywhere St\nFresno, CA 99999", + "localize_sending_time": true, + "send_at": "2015-07-01T10:00:00Z", + "bcc": null, + "created_at": "2015-06-21T10:31:58Z", + "href": "https://api.getdrip.com/v2/9999999/broadcast/123456", + "preview_url": "https://www.getdrip.com/broadcasts/123456/2d83a64861f23b1c35a3b8d6ee3b54f7", + "subject": "4 Marketing Automation Trends for 2015", + "html_body": "HTML body", + "text_body": "Text body", + "links": { + "account": "9999999" + } + } + ] } ``` @@ -605,31 +626,52 @@ const response = await fetch( const body = await response.json(); ``` -> Responds with a 200 OK and the updated Single-Email Campaign if successful: +> Responds with a `200 OK` and the updated Single-Email Campaign if successful: ```json { - "links": { ... }, - "broadcasts": [{ ... }] + "links": { + "broadcasts.account": "https://api.getdrip.com/v2/accounts/{broadcasts.account}" + }, + "broadcasts": [ + { + "id": "123456", + "status": "draft", + "name": "4 Marketing Automation Trends for 2015", + "from_name": "John Doe", + "from_email": "john@example.com", + "postal_address": "123 Anywhere St\nFresno, CA 99999", + "localize_sending_time": true, + "send_at": "2015-07-01T10:00:00Z", + "bcc": null, + "created_at": "2015-06-21T10:31:58Z", + "href": "https://api.getdrip.com/v2/9999999/broadcast/123456", + "preview_url": "https://www.getdrip.com/broadcasts/123456/2d83a64861f23b1c35a3b8d6ee3b54f7", + "subject": "Updated: December Newsletter!", + "html_body": "HTML body", + "text_body": "Text body", + "links": { + "account": "9999999" + } + } + ] } ``` -> If the Single-Email Campaign is not a draft, responds with a 409 Conflict: +> If the Single-Email Campaign is not a draft, responds with a `409 Conflict`: ```json { - "errors": [{ - "code": "conflict_error", - "message": "Cannot update a broadcast with status 'sent'" - }] + "errors": [ + { + "code": "conflict_error", + "message": "Cannot update a broadcast with status 'sent'" + } + ] } ``` -Only Single-Email Campaigns in the `draft` status can be updated via the API; all other -statuses are read-only. Updates are partial: only the fields you include are changed. - -Status, scheduling (`send_at`, `localize_sending_time`), and recipient segmentation cannot -be updated via the API — they are managed through the Drip UI. +Only Single-Email Campaigns in the `draft` status can be updated via the API; all other statuses are read-only. Updates are partial: only the fields you include are changed. Status, scheduling (`send_at`, `localize_sending_time`), and recipient segmentation cannot be updated via the API — they are managed through the Drip UI. ### HTTP Endpoint @@ -647,23 +689,23 @@ be updated via the API — they are managed through the Drip UI. name - Optional. The private name given to the Single-Email Campaign. + The private name given to the Single-Email Campaign. subject - Optional. The email subject line. Supports Liquid templating. + The email subject line. Supports Liquid templating. content - Optional. An Email Content object with html and/or text keys. + An Email Content object with html and/or text keys. preheader - Optional. Preview text shown in email clients before the email is opened. Supports Liquid templating. + Preview text shown in email clients before the email is opened. Supports Liquid templating. postal_address - Optional. A physical mailing address for CAN-SPAM compliance. + A physical mailing address for CAN-SPAM compliance. @@ -710,29 +752,52 @@ const response = await fetch( const body = await response.json(); ``` -> Responds with a 200 OK and the Single-Email Campaign in the deleted status: +> Responds with a `200 OK` and the Single-Email Campaign in the `deleted` status: ```json { - "links": { ... }, - "broadcasts": [{ ... }] + "links": { + "broadcasts.account": "https://api.getdrip.com/v2/accounts/{broadcasts.account}" + }, + "broadcasts": [ + { + "id": "123456", + "status": "deleted", + "name": "4 Marketing Automation Trends for 2015", + "from_name": "John Doe", + "from_email": "john@example.com", + "postal_address": "123 Anywhere St\nFresno, CA 99999", + "localize_sending_time": true, + "send_at": "2015-07-01T10:00:00Z", + "bcc": null, + "created_at": "2015-06-21T10:31:58Z", + "href": "https://api.getdrip.com/v2/9999999/broadcast/123456", + "preview_url": null, + "subject": "4 Marketing Automation Trends for 2015", + "html_body": "HTML body", + "text_body": "Text body", + "links": { + "account": "9999999" + } + } + ] } ``` -> If the Single-Email Campaign is currently sending, responds with a 409 Conflict: +> If the Single-Email Campaign is currently sending, responds with a `409 Conflict`: ```json { - "errors": [{ - "code": "conflict_error", - "message": "Cannot delete a broadcast that is currently sending" - }] + "errors": [ + { + "code": "conflict_error", + "message": "Cannot delete a broadcast that is currently sending" + } + ] } ``` -Deleting is a soft delete: the Single-Email Campaign transitions to the `deleted` status and -is hidden from default listings, but historical data is preserved. Single-Email Campaigns in -the `sending` status cannot be deleted. +Deleting is a soft delete: the Single-Email Campaign transitions to the `deleted` status and is hidden from default listings, but historical data is preserved. Single-Email Campaigns in the `sending` status cannot be deleted. ### HTTP Endpoint @@ -801,29 +866,32 @@ const response = await fetch( // 204 No Content on success ``` -> Responds with a 204 No Content if successful. +> Responds with a `204 No Content` if successful. -> If a recipient is not allowed, responds with a 422 Unprocessable Entity: +> If a recipient is not allowed, responds with a `422 Unprocessable Entity`: ```json { - "errors": [{ - "code": "validation_error", - "attribute": "to_emails", - "message": "test@unknown-domain.com is not a verified domain or active account member" - }] + "errors": [ + { + "code": "validation_error", + "attribute": "to_emails", + "message": "test@unknown-domain.com is not a verified domain or active account member" + } + ] } ``` -> If a rate limit is exceeded, responds with a 429 Too Many Requests and a -> Retry-After header indicating when the limit resets: +> If a rate limit is exceeded, responds with a `429 Too Many Requests` and a `Retry-After` header indicating when the limit resets: ```json { - "errors": [{ - "code": "rate_limit_error", - "message": "Hourly test email limit exceeded. Maximum 20 requests per hour." - }] + "errors": [ + { + "code": "rate_limit_error", + "message": "Hourly test email limit exceeded. Maximum 20 requests per hour." + } + ] } ``` @@ -886,11 +954,11 @@ Rate limits are applied per user. to_emails - Required. An array of 1 to 5 email addresses to send the test to. See recipient validation above. + An array of 1 to 5 email addresses to send the test to. See recipient validation above. preview_as - Optional. The email address of an existing subscriber to use for Liquid template rendering. The test email is personalized as if sent to this subscriber, using their custom fields and tags. If omitted, Liquid variables use placeholder values. + The email address of an existing subscriber to use for Liquid template rendering. The test email is personalized as if sent to this subscriber, using their custom fields and tags. If omitted, Liquid variables use placeholder values. diff --git a/source/includes/rest/_campaigns.md b/source/includes/rest/_campaigns.md index d1d82202491..bac28892978 100644 --- a/source/includes/rest/_campaigns.md +++ b/source/includes/rest/_campaigns.md @@ -28,7 +28,9 @@ "href": "https://api.getdrip.com/v2/9999999/campaigns/123456", "links": { "account": "9999999", - "forms": ["888"] + "forms": [ + "888" + ] } } ``` @@ -77,7 +79,7 @@ postal_address - As required by the CAN-SPAM Act, this is a postal address used for all sent emails. + As required by the CAN-SPAM Act, this is a postal address used for all sent emails. minutes_from_midnight @@ -108,7 +110,7 @@ Deprecated - post_confirmation_url + confirmation_url Deprecated @@ -147,10 +149,6 @@ links An object containing the REST API URL for the account, any associated Email Series Campaign forms and subscribers subscribed to the Email Series Campaign. - - forms - An object containing the associated form created for the Email Series Campaign. This is only populated if a form is created for the Email Series Campaign. Refer to Forms for an overview of the properties returned here. - @@ -197,9 +195,12 @@ client.listCampaigns(options) > The response looks like this: ```json -# The campaigns property is an array of campaign objects. { - "links": { ... }, + "links": { + "campaigns.account": "https://api.getdrip.com/v2/accounts/{campaigns.account}", + "campaigns.form": "https://api.getdrip.com/v2/{campaigns.account}/forms/{campaigns.forms}", + "campaigns.subscribers": "https://api.getdrip.com/v2/{campaigns.account}/campaigns/{campaigns.id}/subscribers" + }, "meta": { "page": 1, "sort": "created_at", @@ -209,7 +210,38 @@ client.listCampaigns(options) "total_count": 5, "status": "all" }, - "campaigns": [ ... ] + "campaigns": [ + { + "id": "123456", + "status": "active", + "name": "SEO Email Course", + "from_name": "John Doe", + "from_email": "john@example.com", + "postal_address": "123 Anywhere St\nFresno, CA 99999", + "minutes_from_midnight": 440, + "localize_sending_time": true, + "days_of_the_week_mask": "0111110", + "start_immediately": true, + "double_optin": true, + "send_to_confirmation_page": false, + "use_custom_confirmation_page": false, + "confirmation_url": null, + "notify_subscribe_email": "derrick@getdrip.com", + "notify_unsubscribe_email": "derrick@getdrip.com", + "bcc": null, + "email_count": 10, + "active_subscriber_count": 320, + "unsubscribed_subscriber_count": 5, + "created_at": "2013-06-21T10:31:58Z", + "href": "https://api.getdrip.com/v2/9999999/campaigns/123456", + "links": { + "account": "9999999", + "forms": [ + "888" + ] + } + } + ] } ``` @@ -286,13 +318,46 @@ client.fetchCampaign(campaignId) > The response looks like this: ```json -# The campaigns property is an array of campaign objects. -# The forms property is an array of forms that feed directly into to the campaign. { - "links": { ... }, - "campaigns": [{ ... }], + "links": { + "campaigns.account": "https://api.getdrip.com/v2/accounts/{campaigns.account}", + "campaigns.form": "https://api.getdrip.com/v2/{campaigns.account}/forms/{campaigns.forms}", + "campaigns.subscribers": "https://api.getdrip.com/v2/{campaigns.account}/campaigns/{campaigns.id}/subscribers" + }, + "campaigns": [ + { + "id": "123456", + "status": "active", + "name": "SEO Email Course", + "from_name": "John Doe", + "from_email": "john@example.com", + "postal_address": "123 Anywhere St\nFresno, CA 99999", + "minutes_from_midnight": 440, + "localize_sending_time": true, + "days_of_the_week_mask": "0111110", + "start_immediately": true, + "double_optin": true, + "send_to_confirmation_page": false, + "use_custom_confirmation_page": false, + "confirmation_url": null, + "notify_subscribe_email": "derrick@getdrip.com", + "notify_unsubscribe_email": "derrick@getdrip.com", + "bcc": null, + "email_count": 10, + "active_subscriber_count": 320, + "unsubscribed_subscriber_count": 5, + "created_at": "2013-06-21T10:31:58Z", + "href": "https://api.getdrip.com/v2/9999999/campaigns/123456", + "links": { + "account": "9999999", + "forms": [ + "888" + ] + } + } + ], "linked": { - "forms": [{ ... }] + "forms": [] } } ``` @@ -451,9 +516,8 @@ client.listAllSubscribesToCampaign(campaignId) > The response looks like this: ```json -# The subscribers property is an array of subscriber objects. { - "links": { ... }, + "links": {}, "meta": { "page": 1, "direction": "desc", @@ -461,7 +525,7 @@ client.listAllSubscribesToCampaign(campaignId) "total_pages": 1, "total_count": 20 }, - "subscribers": [ ... ] + "subscribers": [] } ``` @@ -574,10 +638,11 @@ client.subscribeToCampaign(campaignId, payload) > The response looks like this: ```json -# The subscribers property is an array of one object. { - "links": { ... }, - "subscribers": [{ ... }] + "links": {}, + "subscribers": [ + {} + ] } ``` @@ -629,8 +694,7 @@ client.subscribeToCampaign(campaignId, payload) prospect - Optional. A Boolean specifiying whether we should attach a lead score to the subscriber (when lead scoring is enabled). Defaults to true. - Note: This flag used to be called potential_lead, which we will continue to accept for backwards compatibility. + Optional. A Boolean specifiying whether we should attach a lead score to the subscriber (when lead scoring is enabled). Defaults to true. **Note:** This flag used to be called potential_lead, which we will continue to accept for backwards compatibility. base_lead_score @@ -693,7 +757,6 @@ client.subscriberCampaignSubscriptions(subscriberId) > The response looks like this: ```json -# The Email Series Campaign subscriptions property is an array of campaign subscription objects. { "links": { "campaign_subscriptions.account": "https://api.getdrip.com/v2/accounts/{campaign_subscriptions.account}", @@ -705,25 +768,27 @@ client.subscriberCampaignSubscriptions(subscriberId) "total_pages": 1, "total_count": 5 }, - "campaign_subscriptions": [{ - "id": "123456", - "campaign_id": "999999", - "status": "active", - "is_complete": false, - "lap": 1, - "last_sent_email_index": 0, - "last_sent_email_at": "2016-03-25T11:00:00Z", - "links": { - "account": "9999999", - "subscriber": "z1togz2hcjrkpp5treip" + "campaign_subscriptions": [ + { + "id": "123456", + "campaign_id": "999999", + "status": "active", + "is_complete": false, + "lap": 1, + "last_sent_email_index": 0, + "last_sent_email_at": "2016-03-25T11:00:00Z", + "links": { + "account": "9999999", + "subscriber": "z1togz2hcjrkpp5treip" + } } - }] + ] } ``` ### HTTP Endpoint -`GET /v2/:account_id/subscribers/:subscriber_id/campaign_subscriptions` +`GET /v2/:account_id/subscribers/:subscriber_id/campaign_subscription` ### Arguments diff --git a/source/includes/rest/_subscribers.md b/source/includes/rest/_subscribers.md index 762afc612be..15a36e094e1 100644 --- a/source/includes/rest/_subscribers.md +++ b/source/includes/rest/_subscribers.md @@ -25,7 +25,10 @@ "custom_fields": { "shirt_size": "Medium" }, - "tags": ["Customer", "SEO"], + "tags": [ + "Customer", + "SEO" + ], "ip_address": "111.111.111.11", "user_agent": "Chrome/36.0.1985.143", "original_referrer": "http://www.getdrip.com", @@ -53,8 +56,6 @@ } ``` -**Note:** All subscriber API endpoints only work with your active people. Attempting to modify or delete an inactive person will result in an error. - **Properties** @@ -200,6 +201,7 @@
+**Note:** All subscriber API endpoints only work with your active people. Attempting to modify or delete an inactive person will result in an error. ## Create or update a subscriber @@ -270,21 +272,62 @@ client.createUpdateSubscriber(payload) > The response looks like this: ```json -# The subscribers property is an array of one object. { - "links": { ... }, - "subscribers": [{ ... }] + "links": { + "subscribers.account": "https://api.getdrip.com/v2/accounts/{subscribers.account}" + }, + "subscribers": [ + { + "id": "z1togz2hcjrkpp5treip", + "status": "active", + "email": "john@acme.com", + "first_name": "John", + "last_name": "Doe", + "address1": "123 Main St.", + "address2": "Suite 200", + "city": "Los Angeles", + "state": "CA", + "zip": "90210", + "country": "US", + "phone": "555-555-5555", + "sms_number": "+16125551212", + "sms_consent": true, + "eu_consent": "granted", + "time_zone": "America/Los_Angeles", + "utc_offset": -440, + "visitor_uuid": "sa8f7sdf78sdsdahf788d7asf8sd", + "custom_fields": { + "shirt_size": "Medium" + }, + "tags": [ + "Customer", + "SEO" + ], + "ip_address": "111.111.111.11", + "user_agent": "Chrome/36.0.1985.143", + "original_referrer": "http://www.getdrip.com", + "landing_url": "https://www.getdrip.com/docs/rest-api", + "prospect": true, + "lead_score": 72, + "lifetime_value": 10000, + "created_at": "2013-06-21T10:31:58Z", + "href": "https://api.getdrip.com/v2/9999999/subscribers/12345", + "user_id": "12345", + "base_lead_score": 30, + "links": { + "account": "9999999" + } + } + ] } ``` +If you need to create or update a collection of subscribers at once, use our [batch API](/) instead. **Note:** Concurrently updating the same subscriber via multiple API calls is not supported and will fail with a rate limit error and the message "Too many concurrent requests for the same subscriber". You should retry the call after a short wait period to let the other requests to the same subscriber complete. Triggering this rate limit does not mean you've consumed your overall API rate limited capacity. + ### HTTP Endpoint `POST /v2/:account_id/subscribers` -If you need to create or update a collection of subscribers at once, use our [batch API](/) instead. - -**Note:** Concurrently updating the same subscriber via multiple API calls is not supported and will fail with a rate limit error and the message "Too many concurrent requests for the same subscriber". You should retry the call after a short wait period to let the other requests to the same subscriber complete. Triggering this rate limit does not mean you've consumed your overall API rate limited capacity. - ### Arguments @@ -297,121 +340,119 @@ If you need to create or update a collection of subscribers at once, use our [ba - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
emailOptional. The subscriber's email address. Either email or id or visitor_uuid must be included.The subscriber's email address. Either email or id or visitor_uuid must be included.
idOptional. The subscriber's Drip id. Either email or id or visitor_uuid must be included.The subscriber's Drip id. Either email or id or visitor_uuid must be included.
visitor_uuidOptional. The uuid for a subscriber's visitor record. Either email or id or visitor_uuid must be included.The uuid for a subscriber's visitor record. Either email or id or visitor_uuid must be included.
new_emailOptional. A new email address for the subscriber. If provided and a subscriber with the email above does not exist, this address will be used to create a new subscriber.A new email address for the subscriber. If provided and a subscriber with the email above does not exist, this address will be used to create a new subscriber.
first_nameOptional. The subscriber's first name.The subscriber's first name.
last_nameOptional. The subscriber's last name.The subscriber's last name.
address1Optional. The subscriber's mailing address.The subscriber's mailing address.
address2Optional. An additional field for the subscriber's mailing address.An additional field for the subscriber's mailing address.
cityOptional. The city, town, or village in which the subscriber resides.The city, town, or village in which the subscriber resides.
stateOptional. The region in which the subscriber resides. Typically a province, a state, or a prefecture.The region in which the subscriber resides. Typically a province, a state, or a prefecture.
zipOptional. The postal code in which the subscriber resides, also known as zip, postcode, Eircode, etc.The postal code in which the subscriber resides, also known as zip, postcode, Eircode, etc.
countryOptional. The country in which the subscriber resides.The country in which the subscriber resides.
phoneOptional. The subscriber's primary phone number.The subscriber's primary phone number.
sms_numberOptional. String. The subscriber's mobile phone number in E.164 formatting. E.g. "+16125551212". Only US-based numbers are supported at this time.String. The subscriber's mobile phone number in E.164 formatting. E.g. "+16125551212". Only US-based numbers are supported at this time.
sms_consentOptional. Boolean. true if the person has granted consent to receive marketing and other communication via SMS; false otherwise. Default: false. If you’re unsure whether or not you have gained legal SMS consent, check out our TCPA requirements article.Boolean. true if the person has granted consent to receive marketing and other communication via SMS; false otherwise. Default: false. If you’re unsure whether or not you have gained legal SMS consent, check out our TCPA requirements article.
user_idOptional. A unique identifier for the user in your database, such as a primary key.A unique identifier for the user in your database, such as a primary key.
time_zoneOptional. The subscriber's time zone (in Olson format). Defaults to Etc/UTCThe subscriber's time zone (in Olson format). Defaults to Etc/UTC
lifetime_valueOptional. The lifetime value of the subscriber (in cents).The lifetime value of the subscriber (in cents).
ip_addressOptional. The subscriber's ip address E.g. "111.111.111.11"The subscriber's ip address E.g. "111.111.111.11"
custom_fieldsOptional. An Object containing custom field data. E.g. { "shirt_size": "Medium" }.An Object containing custom field data. E.g. { "shirt_size": "Medium" }.
tagsOptional. An Array containing one or more tags. E.g. ["Customer", "SEO"].An Array containing one or more tags. E.g. ["Customer", "SEO"].
remove_tagsOptional. An Array containing one or more tags to be removed from the subscriber. E.g. ["Customer", "SEO"].An Array containing one or more tags to be removed from the subscriber. E.g. ["Customer", "SEO"].
prospectOptional. A Boolean specifiying whether we should attach a lead score to the subscriber (when lead scoring is enabled). Defaults to true. - Note: This flag used to be called potential_lead, which we will continue to accept for backwards compatibility.A Boolean specifiying whether we should attach a lead score to the subscriber (when lead scoring is enabled). Defaults to true. **Note:** This flag used to be called potential_lead, which we will continue to accept for backwards compatibility.
base_lead_scoreOptional. An Integer specifying the starting value for lead score calculation for this subscriber. Defaults to 30.An Integer specifying the starting value for lead score calculation for this subscriber. Defaults to 30.
eu_consentOptional. A String specifying whether the subscriber granted or denied GDPR consent.A String specifying whether the subscriber granted or denied GDPR consent.
eu_consent_messageOptional. A String containing the message the subscriber granted or denied their consent to.A String containing the message the subscriber granted or denied their consent to.
statusOptional. A String specifying the subscriber's status: either active or unsubscribed.A String specifying the subscriber's status: either active or unsubscribed.
initial_statusOptional. A String specifying the subscriber's known status: either active or unsubscribed. To be used if subscriber's status is unchanged.A String specifying the subscriber's known status: either active or unsubscribed. To be used if subscriber's status is unchanged.
- ## List all subscribers > To list subscribers: @@ -455,17 +496,59 @@ client.listSubscribers(options) > The response looks like this: ```json -# The subscribers property is an array subscriber objects. -# The meta property contains pagination information. { - "links": { ... }, + "links": { + "subscribers.account": "https://api.getdrip.com/v2/accounts/{subscribers.account}" + }, "meta": { "page": 1, "count": 5, "total_pages": 1, "total_count": 5 }, - "subscribers": [{ ... }] + "subscribers": [ + { + "id": "z1togz2hcjrkpp5treip", + "status": "active", + "email": "john@acme.com", + "first_name": "John", + "last_name": "Doe", + "address1": "123 Main St.", + "address2": "Suite 200", + "city": "Los Angeles", + "state": "CA", + "zip": "90210", + "country": "US", + "phone": "555-555-5555", + "sms_number": "+16125551212", + "sms_consent": true, + "eu_consent": "granted", + "time_zone": "America/Los_Angeles", + "utc_offset": -440, + "visitor_uuid": "sa8f7sdf78sdsdahf788d7asf8sd", + "custom_fields": { + "shirt_size": "Medium" + }, + "tags": [ + "Customer", + "SEO" + ], + "ip_address": "111.111.111.11", + "user_agent": "Chrome/36.0.1985.143", + "original_referrer": "http://www.getdrip.com", + "landing_url": "https://www.getdrip.com/docs/rest-api", + "prospect": true, + "lead_score": 72, + "lifetime_value": 10000, + "created_at": "2013-06-21T10:31:58Z", + "href": "https://api.getdrip.com/v2/9999999/subscribers/12345", + "user_id": "12345", + "base_lead_score": 30, + "links": { + "account": "9999999" + } + } + ] } ``` @@ -485,32 +568,27 @@ client.listSubscribers(options) status - Optional. Filter by one of the following statuses: all, active, unsubscribed, active_or_unsubscribed or undeliverable. Defaults to active. + Filter by one of the following statuses: all, active, unsubscribed, active_or_unsubscribed or undeliverable. Defaults to active. - tags - Optional. A comma separated list of tags. When included, returns only subscribers who have at least one of the listed tags. + A comma separated list of tags. When included, returns only subscribers who have at least one of the listed tags. - subscribed_before - Optional. A ISO-8601 datetime. When included, returns only subscribers who were created before the date. Eg. "2017-01-01T00:00:00Z" + A ISO-8601 datetime. When included, returns only subscribers who were created before the date. Eg. "2017-01-01T00:00:00Z" - subscribed_after - Optional. A ISO-8601 datetime. When included, returns only subscribers who were created after the date. Eg. "2016-01-01T00:00:00Z" + A ISO-8601 datetime. When included, returns only subscribers who were created after the date. Eg. "2016-01-01T00:00:00Z" - page - Optional. The page number. Defaults to 1. + The page number. Defaults to 1. - per_page - Optional. The number of records to be returned on each page. Defaults to 100. Maximum 1000. + The number of records to be returned on each page. Defaults to 100. Maximum 1000. @@ -560,16 +638,59 @@ client.fetchSubscriber(idOrEmail) > The response looks like this: ```json -# The subscribers property is an array of one object. { - "links": { ... }, - "subscribers": [{ ... }] + "links": { + "subscribers.account": "https://api.getdrip.com/v2/accounts/{subscribers.account}" + }, + "subscribers": [ + { + "id": "z1togz2hcjrkpp5treip", + "status": "active", + "email": "john@acme.com", + "first_name": "John", + "last_name": "Doe", + "address1": "123 Main St.", + "address2": "Suite 200", + "city": "Los Angeles", + "state": "CA", + "zip": "90210", + "country": "US", + "phone": "555-555-5555", + "sms_number": "+16125551212", + "sms_consent": true, + "eu_consent": "granted", + "time_zone": "America/Los_Angeles", + "utc_offset": -440, + "visitor_uuid": "sa8f7sdf78sdsdahf788d7asf8sd", + "custom_fields": { + "shirt_size": "Medium" + }, + "tags": [ + "Customer", + "SEO" + ], + "ip_address": "111.111.111.11", + "user_agent": "Chrome/36.0.1985.143", + "original_referrer": "http://www.getdrip.com", + "landing_url": "https://www.getdrip.com/docs/rest-api", + "prospect": true, + "lead_score": 72, + "lifetime_value": 10000, + "created_at": "2013-06-21T10:31:58Z", + "href": "https://api.getdrip.com/v2/9999999/subscribers/12345", + "user_id": "12345", + "base_lead_score": 30, + "links": { + "account": "9999999" + } + } + ] } ``` ### HTTP Endpoint -`GET /v2/:account_id/subscribers/:id_or_email_or_visitor_uuid` +`GET /v2/:account_id/subscribers/:id_or_email` ### Arguments @@ -577,12 +698,20 @@ None. ## Remove a subscriber from one or all Email Series Campaigns -> To remove a subscriber from all Email Series s: +> To remove a subscriber from all Email Series Campaigns: ```shell +# To remove a subscriber from all Email Series Campaigns: + curl -X POST "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/subscribers/ID_OR_EMAIL/remove" \ -H 'User-Agent: Your App Name (www.yourapp.com)' \ -u YOUR_API_KEY: + +# To remove a subscriber from a specific campaign: + +curl -X POST "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/subscribers/ID_OR_EMAIL/remove?campaign_id=CAMPAIGN_ID" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: ``` ```ruby @@ -636,30 +765,65 @@ client.unsubscribeFromCampaign(idOrEmail, campaignId) }); ``` -> To remove a subscriber from a specific campaign: - -```shell -curl -X POST "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/subscribers/ID_OR_EMAIL/remove?campaign_id=CAMPAIGN_ID" \ - -H 'User-Agent: Your App Name (www.yourapp.com)' \ - -u YOUR_API_KEY: -``` - > The response looks like this: ```json -# The subscribers property is an array of one object. { - "links": { ... }, - "subscribers": [{ ... }] + "links": { + "subscribers.account": "https://api.getdrip.com/v2/accounts/{subscribers.account}" + }, + "subscribers": [ + { + "id": "z1togz2hcjrkpp5treip", + "status": "active", + "email": "john@acme.com", + "first_name": "John", + "last_name": "Doe", + "address1": "123 Main St.", + "address2": "Suite 200", + "city": "Los Angeles", + "state": "CA", + "zip": "90210", + "country": "US", + "phone": "555-555-5555", + "sms_number": "+16125551212", + "sms_consent": true, + "eu_consent": "granted", + "time_zone": "America/Los_Angeles", + "utc_offset": -440, + "visitor_uuid": "sa8f7sdf78sdsdahf788d7asf8sd", + "custom_fields": { + "shirt_size": "Medium" + }, + "tags": [ + "Customer", + "SEO" + ], + "ip_address": "111.111.111.11", + "user_agent": "Chrome/36.0.1985.143", + "original_referrer": "http://www.getdrip.com", + "landing_url": "https://www.getdrip.com/docs/rest-api", + "prospect": true, + "lead_score": 72, + "lifetime_value": 10000, + "created_at": "2013-06-21T10:31:58Z", + "href": "https://api.getdrip.com/v2/9999999/subscribers/12345", + "user_id": "12345", + "base_lead_score": 30, + "links": { + "account": "9999999" + } + } + ] } ``` +This endpoint was previously labeled `unsubscribe`. + ### HTTP Endpoint `POST /v2/:account_id/subscribers/:id_or_email/remove` -This endpoint was previously labeled `unsubscribe`. - ### Arguments @@ -672,7 +836,7 @@ This endpoint was previously labeled `unsubscribe`. - +
campaign_idOptional. The Email Series Campaign from which to remove the subscriber. Defaults to all.The Email Series Campaign from which to remove the subscriber. Defaults to all.
@@ -721,10 +885,53 @@ client.unsubscribeFromAllMailings(idOrEmail) > The response looks like this: ```json -# The subscribers property is an array of one object. { - "links": { ... }, - "subscribers": [{ ... }] + "links": { + "subscribers.account": "https://api.getdrip.com/v2/accounts/{subscribers.account}" + }, + "subscribers": [ + { + "id": "z1togz2hcjrkpp5treip", + "status": "unsubscribed", + "email": "john@acme.com", + "first_name": "John", + "last_name": "Doe", + "address1": "123 Main St.", + "address2": "Suite 200", + "city": "Los Angeles", + "state": "CA", + "zip": "90210", + "country": "US", + "phone": "555-555-5555", + "sms_number": "+16125551212", + "sms_consent": true, + "eu_consent": "granted", + "time_zone": "America/Los_Angeles", + "utc_offset": -440, + "visitor_uuid": "sa8f7sdf78sdsdahf788d7asf8sd", + "custom_fields": { + "shirt_size": "Medium" + }, + "tags": [ + "Customer", + "SEO" + ], + "ip_address": "111.111.111.11", + "user_agent": "Chrome/36.0.1985.143", + "original_referrer": "http://www.getdrip.com", + "landing_url": "https://www.getdrip.com/docs/rest-api", + "prospect": true, + "lead_score": 72, + "lifetime_value": 10000, + "created_at": "2013-06-21T10:31:58Z", + "href": "https://api.getdrip.com/v2/9999999/subscribers/12345", + "user_id": "12345", + "base_lead_score": 30, + "links": { + "account": "9999999" + } + } + ] } ``` diff --git a/source/includes/rest/_workflows.md b/source/includes/rest/_workflows.md index 4bc1e76f7d9..18325d268f7 100644 --- a/source/includes/rest/_workflows.md +++ b/source/includes/rest/_workflows.md @@ -105,9 +105,10 @@ client.listAllWorkflows(options) > The response looks like this: ```json -# The workflows property is an array of workflow objects. { - "links": { ... }, + "links": { + "workflows.account": "https://api.getdrip.com/v2/accounts/{workflows.account}" + }, "meta": { "page": 1, "sort": "sort_order", @@ -117,7 +118,18 @@ client.listAllWorkflows(options) "total_count": 5, "status": "all" }, - "workflows": [ ... ] + "workflows": [ + { + "id": "123456", + "href": "https://api.getdrip.com/v2/9999999/workflows/123456", + "name": "Main Funnel", + "status": "active", + "created_at": "2016-07-01T10:00:00Z", + "links": { + "account": "9999999" + } + } + ] } ``` @@ -137,15 +149,15 @@ client.listAllWorkflows(options) status - Optional. Filter by one of the following statuses: draft, active, or paused. Defaults to all. + Filter by one of the following statuses: draft, active, or paused. Defaults to all. sort - Optional. Sort results by one of these fields: created_at or name. Defaults to created_at. + Sort results by one of these fields: created_at or name. Defaults to created_at. direction - Optional. Filter sort direction with: asc or desc. Defaults to asc. + Filter sort direction with: asc or desc. Defaults to asc. @@ -194,10 +206,22 @@ client.fetchWorkflow(workflowId) > The response looks like this: ```json -# The workflows property is an array of one workflow object. { - "links": { ... }, - "workflows": [{ ... }] + "links": { + "workflows.account": "https://api.getdrip.com/v2/accounts/{workflows.account}" + }, + "workflows": [ + { + "id": "123456", + "href": "https://api.getdrip.com/v2/9999999/workflows/123456", + "name": "Main Funnel", + "status": "active", + "created_at": "2016-07-01T10:00:00Z", + "links": { + "account": "9999999" + } + } + ] } ``` @@ -313,7 +337,7 @@ None. ## Start someone on a workflow -> To start a someone on a workflow: +> To start someone on a workflow: ```shell curl -X POST "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/workflows/WORKFLOW_ID/subscribers" \ @@ -383,10 +407,11 @@ client.startOnWorkflow(workflowId, payload) > The response looks like this: ```json -# The subscribers property is an array of one object. { - "links": { ... }, - "subscribers": [{ ... }] + "links": {}, + "subscribers": [ + {} + ] } ``` @@ -408,76 +433,75 @@ If the workflow is not active, the subscriber will not be added to the workflow. email - Optional. The subscriber's email address. Either email or id must be included. + The subscriber's email address. Either email or id must be included. id - Optional. The subscriber's Drip id. Either email or id must be included. + The subscriber's Drip id. Either email or id must be included. first_name - Optional. The subscriber's first name. + The subscriber's first name. last_name - Optional. The subscriber's last name. + The subscriber's last name. address1 - Optional. The subscriber's mailing address. + The subscriber's mailing address. address2 - Optional. An additional field for the subscriber's mailing address. + An additional field for the subscriber's mailing address. city - Optional. The city, town, or village in which the subscriber resides. + The city, town, or village in which the subscriber resides. state - Optional. The region in which the subscriber resides. Typically a province, a state, or a prefecture. + The region in which the subscriber resides. Typically a province, a state, or a prefecture. zip - Optional. The postal code in which the subscriber resides, also known as zip, postcode, Eircode, etc. + The postal code in which the subscriber resides, also known as zip, postcode, Eircode, etc. country - Optional. The country in which the subscriber resides. + The country in which the subscriber resides. phone - Optional. The subscriber's primary phone number. + The subscriber's primary phone number. user_id - Optional. A unique identifier for the user in your database, such as a primary key. + A unique identifier for the user in your database, such as a primary key. time_zone - Optional. The subscriber's time zone (in Olson format). Defaults to Etc/UTC + The subscriber's time zone (in Olson format). Defaults to Etc/UTC. custom_fields - Optional. An Object containing custom field data. E.g. { "shirt_size": "Medium" }. + An Object containing custom field data. E.g. { "shirt_size": "Medium" }. tags - Optional. An Array containing one or more tags. E.g. ["Customer", "SEO"]. + An Array containing one or more tags. E.g. ["Customer", "SEO"]. prospect - Optional. A Boolean specifiying whether we should attach a lead score to the subscriber (when lead scoring is enabled). Defaults to true. - Note: This flag used to be called potential_lead, which we will continue to accept for backwards compatibility. + A Boolean specifiying whether we should attach a lead score to the subscriber (when lead scoring is enabled). Defaults to true. **Note:** This flag used to be called potential_lead, which we will continue to accept for backwards compatibility. eu_consent - Optional. A String specifying whether the subscriber granted or denied GDPR consent. + A String specifying whether the subscriber granted or denied GDPR consent. eu_consent_message - Optional. A String containing the message the subscriber granted or denied their consent to. + A String containing the message the subscriber granted or denied their consent to. @@ -719,15 +743,15 @@ client.createTrigger(workflowId, payload) provider - Required. A String indicating a provider. + A String indicating a provider. trigger_type - Required. A String indicating the automation trigger type. + A String indicating the automation trigger type. properties - Optional. An Object containing properties for the given trigger. + An Object containing properties for the given trigger. @@ -841,15 +865,15 @@ client.updateTrigger(workflowId, triggerId, payload) provider - Required. A String indicating a provider. + A String indicating a provider. trigger_type - Required. A String indicating the automation trigger type. + A String indicating the automation trigger type. properties - Optional. An Object containing properties for the given trigger. + An Object containing properties for the given trigger. diff --git a/spec/README.md b/spec/README.md new file mode 100644 index 00000000000..ff6c9721ea1 --- /dev/null +++ b/spec/README.md @@ -0,0 +1,107 @@ +# REST API spec (OpenAPI source of truth) + +The Drip REST API is described as OpenAPI 3.1 in this directory. The spec is the +**source of truth**; the public Slate docs at `source/includes/rest/_*.md` are +**generated** from it. Two renderers consume the same spec: + +- **Redoc** renders the whole API from the aggregator `rest-v2.yaml` (default + OpenAPI viewer). +- **The custom generator** (`script/generate-docs`) renders each fragment into + the Slate markdown partial the public docs site (developer.drip.com) ships, + so the published docs stay byte-for-byte the same during the migration. + +## Layout + +``` +spec/ + rest-v2.yaml # aggregator: info + servers + security + tags, $refs every path + components.yaml # shared parameters / responses / schemas / securitySchemes + fragments/ # one file per resource (Tags, Accounts, …) + tags.yaml + accounts.yaml + ... +``` + +Each fragment is self-contained: it declares its `tags`, `paths`, and any +resource-specific `components.schemas`, and references shared pieces via a +relative pointer such as `$ref: "../components.yaml#/components/responses/Error"`. +That keeps a fragment renderable on its own by the generator while still +bundling cleanly into `rest-v2.yaml` for Redoc. + +## Commands + +```bash +# Redoc (default renderer) +npx @redocly/cli preview-docs spec/rest-v2.yaml # live preview +npx @redocly/cli lint spec/rest-v2.yaml # validate +npx @redocly/cli bundle spec/rest-v2.yaml -o out.yaml # single-file bundle + +# Slate markdown generation +ruby script/generate-docs # regenerate every source/includes/rest/_*.md +ruby script/generate-docs tags users # regenerate only named resources +ruby script/verify-docs # check generated output matches committed md +ruby script/verify-docs --diff tags # show a unified diff for mismatches +``` + +`script/verify-docs` reports two results per resource: + +- **BYTE** — generated output is byte-identical to the committed `_.md`. +- **NORM** — output matches after collapsing insignificant whitespace. + +NORM is the gate (non-zero exit on failure). BYTE failures are reported but do +not fail the run, because a handful of published pages contain intentional +hand-formatting (and a few historical typos/whitespace quirks) that the spec +deliberately does not reproduce. + +## The `x-slate` presentation layer + +OpenAPI structural data (paths, parameters, schemas, responses, examples, +`x-codeSamples`) is what Redoc renders. The Slate pages additionally carry prose +and formatting that cannot be derived from that structure — intro annotations, +hand-formatted JSON, resource overview blocks. Those live under the `x-slate` +vendor extension and are consumed only by the generator (Redoc ignores unknown +`x-` keys). + +On a **tag** (resource) object, under `x-slate`: + +| Key | Effect | +| -------- | ------ | +| `title` | Override the `# …` H1 text (e.g. "Orders (Legacy)" where it differs from the Redoc tag name). | +| `lead` | Raw markdown inserted right after the H1 (the representation JSON + Properties table, intro asides, etc.). | +| `represent` | Structured alternative to a raw `lead`: a list of annotated example blocks, each `{intro, schema}` (renders the schema's example as JSON) or `{intro, json}` (raw JSON). | +| `properties` | Render a **Properties** table from a named schema's properties (used with `represent`). | +| `lead_extra` | Raw markdown appended after the auto-generated `represent`/`properties` lead (e.g. a conceptual sub-section like "Email Content"). | +| `footer` | Raw markdown appended after the last operation (e.g. an "Order events" reference table). | + +On an **operation** object, under `x-slate`: + +| Key | Effect | +| ------------------ | ------ | +| `order` | Integer sort key to reorder operations on the page (the published pages sometimes interleave operations from different paths). Defaults to document order. | +| `code-order` | List of languages to order the code-sample tabs (e.g. `[ruby, shell]`). Defaults to authored order. | +| `intro` | Text for the `> …` annotation above the request code samples. | +| `response.intro` | Text for the `> …` annotation above the response block. | +| `response.body` | Raw JSON for the response block. Omit to derive it from the response `example` (pretty-printed). Use this for the intentionally hand-formatted/abbreviated bodies (`{ ... }`, compact arrays). | +| `response.status` | Render the example for a specific response status (e.g. `"422"`) instead of the default success example. | +| `response` (list) | `response` may also be a list of blocks, to show a success example followed by documented error examples. Each item takes `intro`, plus `status` or `body`. | +| `sections` | Raw markdown inserted after the response block and before `### HTTP Endpoint` (e.g. an extra Properties table). | +| `endpoint` | Override the `### HTTP Endpoint` line. Omit to derive `METHOD /v2/` with `{param}` → `:param`. | +| `arguments` | `none` (→ "None."), `auto` (derive a Key/Description table from the request body / query params), or a raw HTML/markdown string. Defaults to `auto`. | +| `show-description` | `true` to also surface the operation `description` as an inline paragraph (Redoc shows it in its own panel regardless). | + +Language tabs render in the order the `x-codeSamples` entries are authored +(deduped by language), so author them shell → ruby → javascript except where a +published page differs (e.g. Orders lists Ruby before shell). + +### Adding or changing an endpoint + +1. Edit the resource fragment in `spec/fragments/` (structural data first). +2. Add/adjust `x-slate` keys to match the desired Slate page. +3. `ruby script/generate-docs ` and review the diff. +4. `ruby script/verify-docs ` until NORM passes (BYTE if you need + exact parity with an existing published page). +5. `npx @redocly/cli lint spec/rest-v2.yaml` to confirm the aggregator is valid. + +To add a brand-new resource, also add its tag (in display order) and its paths +to `rest-v2.yaml`, and add `` to `includes_rest_api` in +`source/index.html.md`. diff --git a/spec/components.yaml b/spec/components.yaml new file mode 100644 index 00000000000..13c10b1b333 --- /dev/null +++ b/spec/components.yaml @@ -0,0 +1,92 @@ +# Shared OpenAPI components referenced across every resource fragment. +# +# Fragments reference these with a relative external pointer, e.g. +# $ref: "../components.yaml#/components/responses/Error" +# so that each fragment is self-contained when run through the markdown +# generator on its own, and Redocly resolves the same refs when bundling +# spec/rest-v2.yaml for the Redoc preview. +openapi: 3.1.0 +info: + title: Drip REST API — shared components + version: "2" +paths: {} +components: + parameters: + accountId: + name: account_id + in: path + required: true + description: The ID of the Drip account. + schema: + type: string + + responses: + Error: + description: | + `401 Unauthorized` for anonymous requests to authenticated resources, + `403 Forbidden` for insufficient permissions or disallowed state + transitions, `404 Not Found` if the resource does not exist. + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + example: + errors: + - code: authorization_error + message: You are not authorized to access this resource + ValidationError: + description: | + `422 Unprocessable Entity` with an errors object detailing which + attributes are invalid (e.g. `presence_error`, `length_error`, + `email_error`). + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + example: + errors: + - code: presence_error + attribute: email + message: Email is required + + schemas: + ErrorResponse: + type: object + properties: + errors: + type: array + items: + type: object + required: [code] + properties: + code: + type: string + description: Machine-readable error code, e.g. `presence_error`. + attribute: + type: string + description: The invalid attribute, for validation errors. + message: + type: string + description: Human-readable error message. + + securitySchemes: + apiKey: + type: http + scheme: basic + description: | + For private integrations. Your personal API Token + ([found here](https://www.getdrip.com/user/edit)) is the username portion + of the Basic Authentication scheme, with an empty password. + oauth2: + type: oauth2 + description: | + For public integrations — e.g. allowing your customers to authorize + access to their Drip accounts from your application. Register your + application at . Tokens do + not expire. + flows: + authorizationCode: + authorizationUrl: https://www.getdrip.com/oauth/authorize + tokenUrl: https://www.getdrip.com/oauth/token + scopes: + public: Default scope. diff --git a/spec/fragments/accounts.yaml b/spec/fragments/accounts.yaml new file mode 100644 index 00000000000..ed04cecd165 --- /dev/null +++ b/spec/fragments/accounts.yaml @@ -0,0 +1,323 @@ +tags: + - name: Accounts + description: The Drip accounts the authenticated user has access to. + x-slate: + lead: | + > Accounts are represented as follows: + + ```json + { + "id": "9999999", + "name": "Acme, Inc.", + "url": "www.acme.com", + "default_from_name": "John Doe", + "default_from_email": "john@acme.com", + "default_postal_address": "123 Anywhere St\nFresno, CA 99999", + "primary_email": "john@acme.com", + "enable_third_party_cookies": false, + "phone_number": "999-999-9999", + "created_at": "2013-06-21T10:31:58Z", + "href": "https://api.getdrip.com/v2/accounts/9999999" + } + ``` + + > All responses containing account data also include the following top-level link data: + + ```json + { + "links": { + "accounts.broadcasts": "https://api.getdrip.com/v2/{accounts.id}/broadcasts", + "accounts.campaigns": "https://api.getdrip.com/v2/{accounts.id}/campaigns", + "accounts.goals": "https://api.getdrip.com/v2/{accounts.id}/goals" + } + } + ``` + + **Properties** + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyDescription
idA read-only Drip generated unique id used to identify each subscriber record.
nameThe name assigned to each account. Defaults to the account's website URL.
urlThe account's website URL.
default_from_nameA default "from name" that appears in your sent emails and can be changed on a per email basis.
default_from_emailA default "from email" that appears in your sent emails and can be changed on a per email basis.
default_postal_addressAs required by the CAN-SPAM Act, this is a default postal address used for all sent emails and can be changed on a per email basis.
primary_emailThe account owner's email address.
enable_third_party_cookiesWhen enabled allows tracking visitors across multiple domains (e.g. you use a shopping cart system hosted on a different site).
phone_numberThe account's primary contact number.
created_atA read-only Drip generated timestamp for when the account was first created.
hrefThe url designated for retrieving the account record via the REST API.
linksAn object containing URLs for campaign and conversion resources. Note, conversions are represented as "Goals".
+paths: + /accounts: + get: + operationId: listAccounts + tags: [Accounts] + summary: List all accounts + x-slate: + intro: "To list all accounts the authenticated user has access to:" + response: + intro: "The response looks like this:" + body: | + # The accounts property is an array of account objects. + { + "links": { ... }, + "accounts": [ ... ] + } + arguments: none + responses: + "200": + description: All accounts the authenticated user has access to. The `accounts` property is an array of account objects. + content: + application/json: + schema: + type: object + required: [links, accounts] + properties: + links: + type: object + description: An object containing URLs for campaign and conversion resources. Note, conversions are represented as "Goals". + accounts: + type: array + description: An array of account objects. + items: + $ref: "#/components/schemas/Account" + example: + links: + accounts.broadcasts: "https://api.getdrip.com/v2/{accounts.id}/broadcasts" + accounts.campaigns: "https://api.getdrip.com/v2/{accounts.id}/campaigns" + accounts.goals: "https://api.getdrip.com/v2/{accounts.id}/goals" + accounts: + - id: "9999999" + name: "Acme, Inc." + url: www.acme.com + default_from_name: John Doe + default_from_email: john@acme.com + default_postal_address: "123 Anywhere St\nFresno, CA 99999" + primary_email: john@acme.com + enable_third_party_cookies: false + phone_number: 999-999-9999 + created_at: "2013-06-21T10:31:58Z" + href: https://api.getdrip.com/v2/accounts/9999999 + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl "https://api.getdrip.com/v2/accounts" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + response = client.accounts + + if response.success? + puts response.body["accounts"] + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + + client.listAccounts() + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + + /accounts/{account_id}: + parameters: + - name: account_id + in: path + required: true + description: The ID of the account to fetch. + schema: + type: string + + get: + operationId: fetchAccount + tags: [Accounts] + summary: Fetch an account + x-slate: + intro: "To fetch a specific account:" + response: + intro: "The response looks like this:" + body: | + # The accounts property is an array of one account object. + { + "links": { ... }, + "accounts": [{ ... }] + } + arguments: none + responses: + "200": + description: The requested account. The `accounts` property is an array of one account object. + content: + application/json: + schema: + type: object + required: [links, accounts] + properties: + links: + type: object + description: An object containing URLs for campaign and conversion resources. Note, conversions are represented as "Goals". + accounts: + type: array + description: An array of one account object. + items: + $ref: "#/components/schemas/Account" + example: + links: + accounts.broadcasts: "https://api.getdrip.com/v2/{accounts.id}/broadcasts" + accounts.campaigns: "https://api.getdrip.com/v2/{accounts.id}/campaigns" + accounts.goals: "https://api.getdrip.com/v2/{accounts.id}/goals" + accounts: + - id: "9999999" + name: "Acme, Inc." + url: www.acme.com + default_from_name: John Doe + default_from_email: john@acme.com + default_postal_address: "123 Anywhere St\nFresno, CA 99999" + primary_email: john@acme.com + enable_third_party_cookies: false + phone_number: 999-999-9999 + created_at: "2013-06-21T10:31:58Z" + href: https://api.getdrip.com/v2/accounts/9999999 + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl "https://api.getdrip.com/v2/accounts/ACCOUNT_ID" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + account_id = 9999999 + response = client.account(account_id) + + if response.success? + puts response.body + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + + client.fetchAccount(accountId) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); +components: + schemas: + Account: + type: object + properties: + id: + type: string + description: A read-only Drip generated unique id used to identify each subscriber record. + name: + type: string + description: The name assigned to each account. Defaults to the account's website URL. + url: + type: string + description: The account's website URL. + default_from_name: + type: string + description: A default "from name" that appears in your sent emails and can be changed on a per email basis. + default_from_email: + type: string + format: email + description: A default "from email" that appears in your sent emails and can be changed on a per email basis. + default_postal_address: + type: string + description: As required by the [CAN-SPAM Act](http://1.usa.gov/YgrzFP), this is a default postal address used for all sent emails and can be changed on a per email basis. + primary_email: + type: string + format: email + description: The account owner's email address. + enable_third_party_cookies: + type: boolean + description: When enabled allows tracking visitors across multiple domains (e.g. you use a shopping cart system hosted on a different site). + phone_number: + type: string + description: The account's primary contact number. + created_at: + type: string + format: date-time + description: A read-only Drip generated timestamp for when the account was first created. + href: + type: string + description: The url designated for retrieving the account record via the REST API. diff --git a/spec/fragments/batch_api.yaml b/spec/fragments/batch_api.yaml new file mode 100644 index 00000000000..a6633506587 --- /dev/null +++ b/spec/fragments/batch_api.yaml @@ -0,0 +1,1403 @@ +tags: + - name: Batch API + description: | + Batch endpoints for creating or updating collections of records in a single + request. Each batch accepts between 1 and 1000 objects. Since the batch APIs + process requests in the background, there may be a delay between the time you + submit your request and the time your data appears in the user interface. + + x-slate: + footer: | + ## Create or update a batch of carts + + > To create or update a batch of carts: + + ```shell + curl -X POST "https://api.getdrip.com/v3/YOUR_ACCOUNT_ID/shopper_activity/cart/batch" \ + -H "Content-Type: application/json" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: \ + -d @- << EOF + { + "carts": [ + { + "provider": "my_custom_platform", + "email": "user@gmail.com", + "action": "created", + "cart_id": "456445746", + "occurred_at": "2019-01-17T20:50:00Z", + "cart_public_id": "#5", + "grand_total": 16.99, + "total_discounts": 5.34, + "currency": "USD", + "cart_url": "https://mysuperstore.com/cart/456445746", + "items": [ + { + "product_id": "B01J4SWO1G", + "product_variant_id": "B01J4SWO1G-CW-BOTT", + "sku": "XHB-1234", + "name": "The Coolest Water Bottle", + "brand": "Drip", + "categories": [ + "Accessories" + ], + "price": 11.16, + "quantity": 2, + "discounts": 5.34, + "total": 16.99, + "product_url": "https://mysuperstore.com/dp/B01J4SWO1G", + "image_url": "https://www.getdrip.com/images/example_products/water_bottle.png", + "product_tag": "Best Seller" + } + ] + }, + { + "provider": "my_custom_platform", + "email": "john@acme.com", + "action": "created", + "occurred_at": "2019-03-12T15:31:58Z", + "cart_id": "63754763", + "cart_public_id": "#4", + "grand_total": 54.00, + "total_discounts": 1.00, + "currency": "USD", + "order_url": "http://myorders.com/orders/4", + "items": [{ + "product_id": "B01F9AQ99M", + "product_variant_id": "B01F9AQ99M-YEL-BT", + "sku": "JDT-4321", + "name": "Yellow Boots of Might", + "brand": "Drip", + "categories": [ + "Of Might", + "Outdoors" + ], + "price": 55.00, + "quantity": 1, + "discounts": 1.00, + "total": 54.00, + "color": "black", + "product_url": "https://mysuperstore.com/dp/B01F9AQ99M", + "image_url": "https://www.getdrip.com/images/example_products/boots.png" + }] + } + ] + } + EOF + ``` + + > Responds with a 202 Accepted if successful. That means the server accepted the request and queued it for processing. The response includes a list of unique request_ids that can be used to check the status of the request later on: + + ```json + { + "request_ids": [ + "db8a7b16-32dd-4863-8b6e-818e3eaab99a", + "002048e9-3692-4f0a-8bbf-80a077acf642" + ] + } + ``` + + + We recommend using this API endpoint when you need to import a collection of carts that will likely exceed the regular rate limit of 3,600 requests per hour. + + Note: Since our batch APIs process requests in the background, there may be a delay between the time you submit your + request and the time your data appears in the user interface. + + ### HTTP Endpoint + + `POST /v3/:account_id/shopper_activity/cart/batch` + + ### Arguments + + + + + + + + + + + + + + +
KeyDescription
cartsRequired. An Array with between 1 and 1000 objects containing cart activity.
+ + ## Create or update a batch of orders + + > To create or update a batch of orders: + + ```shell + curl -X POST "https://api.getdrip.com/v3/YOUR_ACCOUNT_ID/shopper_activity/order/batch" \ + -H "Content-Type: application/json" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: \ + -d @- << EOF + { + "orders": [ + { + "provider": "my_custom_platform", + "email": "john@acme.com", + "action": "placed", + "occurred_at": "2019-03-12T15:31:58Z", + "order_id": "63754763", + "order_public_id": "#4", + "grand_total": 54.00, + "total_discounts": 1.00, + "total_taxes": 1.00, + "total_fees": 0, + "total_shipping": 5.00, + "currency": "USD", + "order_url": "http://myorders.com/orders/4", + "items": [{ + "product_id": "B01F9AQ99M", + "product_variant_id": "B01F9AQ99M-YEL-BT", + "sku": "JDT-4321", + "name": "Yellow Boots of Might", + "brand": "Drip", + "categories": [ + "Of Might", + "Outdoors" + ], + "price": 49.00, + "quantity": 1, + "discounts": 1.00, + "taxes": 1.00, + "fees": 0, + "shipping": 5.00, + "total": 54.00, + "color": "black", + "product_url": "https://mysuperstore.com/dp/B01F9AQ99M", + "image_url": "https://www.getdrip.com/images/example_products/boots.png" + }], + "billing_address": { + "label": "Primary Billing", + "first_name": "Bill", + "last_name": "Billington", + "company": "Bills R US", + "address_1": "123 Bill St.", + "address_2": "Apt. B", + "city": "Billtown", + "state": "CA", + "postal_code": "01234", + "country": "United States", + "phone": "555-555-5555" + }, + "shipping_address": { + "label": "Downtown Office", + "first_name": "Ship", + "last_name": "Shipington", + "company": "Shipping 4 Less", + "address_1": "123 Ship St.", + "address_2": "null", + "city": "Shipville", + "state": "CA", + "postal_code": "01234", + "country": "United States", + "phone": "555-555-5555" + } + }, + { + "provider": "my_custom_platform", + "email": "user@gmail.com", + "action": "updated", + "occurred_at": "2019-03-12T15:27:16Z", + "order_id": "456445746", + "order_public_id": "#5", + "grand_total": 21.48, + "total_discounts": 5.34, + "total_taxes": 1.00, + "total_fees": 2.00, + "total_shipping": 5.00, + "currency": "USD", + "order_url": "https://mysuperstore.com/order/5", + "items": [ + { + "product_id": "B01J4SWO1G", + "product_variant_id": "B01J4SWO1G-CW-BOTT", + "sku": "XHB-1234", + "name": "The Coolest Water Bottle", + "brand": "Drip", + "categories": [ + "Accessories" + ], + "price": 11.16, + "sale_price": 10.16, + "quantity": 2, + "discounts": 5.34, + "taxes": 1.00, + "fees": 0.50, + "shipping": 5.00, + "total": 21.48, + "product_url": "https://mysuperstore.com/dp/B01J4SWO1G", + "image_url": "https://www.getdrip.com/images/example_products/water_bottle.png", + "product_tag": "Best Seller" + } + ], + "billing_address": { + "label": "Primary Billing", + "first_name": "Bill", + "last_name": "Billington", + "company": "Bills R US", + "address_1": "123 Bill St.", + "address_2": "Apt. B", + "city": "Billtown", + "state": "CA", + "postal_code": "01234", + "country": "United States", + "phone": "555-555-5555" + }, + "shipping_address": { + "label": "Downtown Office", + "first_name": "Ship", + "last_name": "Shipington", + "company": "Shipping 4 Less", + "address_1": "123 Ship St.", + "city": "Shipville", + "state": "CA", + "postal_code": "01234", + "country": "United States", + "phone": "555-555-5555" + } + } + ] + } + EOF + ``` + + ```ruby + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + response = client.create_order_activity_events( + [ + { + provider: "my_custom_platform", + email: "john@acme.com", + action: "placed", + occurred_at: "2019-03-12T15:31:58Z", + order_id: "63754763", + order_public_id: "#4", + grand_total: 54.00, + total_discounts: 1.00, + total_taxes: 1.00, + total_fees: 0, + total_shipping: 5.00, + currency: "USD", + order_url: "http://myorders.com/orders/4", + items: [{ + product_id: "B01F9AQ99M", + product_variant_id: "B01F9AQ99M-YEL-BT", + sku: "JDT-4321", + name: "Yellow Boots of Might", + brand: "Drip", + categories: [ + "Of Might", + "Outdoors" + ], + price: 49.00, + quantity: 1, + discounts: 1.00, + taxes: 1.00, + fees: 0, + shipping: 5.00, + total: 54.00, + color: "black", + product_url: "https://mysuperstore.com/dp/B01F9AQ99M", + image_url: "https://www.getdrip.com/images/example_products/boots.png" + }], + billing_address: { + label: "Primary Billing", + first_name: "Bill", + last_name: "Billington", + company: "Bills R US", + address_1: "123 Bill St.", + address_2: "Apt. B", + city: "Billtown", + state: "CA", + postal_code: "01234", + country: "United States", + phone: "555-555-5555" + }, + shipping_address: { + label: "Downtown Office", + first_name: "Ship", + last_name: "Shipington", + company: "Shipping 4 Less", + address_1: "123 Ship St.", + address_2: "null", + city: "Shipville", + state: "CA", + postal_code: "01234", + country: "United States", + phone: "555-555-5555" + } + }, + { + provider: "my_custom_platform", + email: "user@gmail.com", + action: "updated", + occurred_at: "2019-03-12T15:27:16Z", + order_id: "456445746", + order_public_id: "#5", + grand_total: 21.48, + total_discounts: 5.34, + total_taxes: 1.00, + total_fees: 2.00, + total_shipping: 5.00, + currency: "USD", + order_url: "https://mysuperstore.com/order/5", + items: [ + { + product_id: "B01J4SWO1G", + product_variant_id: "B01J4SWO1G-CW-BOTT", + sku: "XHB-1234", + name: "The Coolest Water Bottle", + brand: "Drip", + categories: [ + "Accessories" + ], + price: 11.16, + sale_price: 10.16, + quantity: 2, + discounts: 5.34, + taxes: 1.00, + fees: 0.50, + shipping: 5.00, + total: 21.48, + product_url: "https://mysuperstore.com/dp/B01J4SWO1G", + image_url: "https://www.getdrip.com/images/example_products/water_bottle.png", + product_tag: "Best Seller" + } + ], + billing_address: { + label: "Primary Billing", + first_name: "Bill", + last_name: "Billington", + company: "Bills R US", + address_1: "123 Bill St.", + address_2: "Apt. B", + city: "Billtown", + state: "CA", + postal_code: "01234", + country: "United States", + phone: "555-555-5555" + }, + shipping_address: { + label: "Downtown Office", + first_name: "Ship", + last_name: "Shipington", + company: "Shipping 4 Less", + address_1: "123 Ship St.", + city: "Shipville", + state: "CA", + postal_code: "01234", + country: "United States", + phone: "555-555-5555" + } + } + ] + ) + + if response.success? + puts "Request accepted" + else + puts "Error occurred" + end + ``` + + > Responds with a 202 Accepted if successful. That means the server accepted the request and queued it for processing. The response includes a list of unique request_ids that can be used to check the status of the request later on: + + ```json + { + "request_ids": [ + "db8a7b16-32dd-4863-8b6e-818e3eaab99a", + "002048e9-3692-4f0a-8bbf-80a077acf642" + ] + } + ``` + + + We recommend using this API endpoint when you need to import a collection of orders that will likely exceed the regular rate limit of 3,600 requests per hour. + + Note: Since our batch APIs process requests in the background, there may be a delay between the time you submit your + request and the time your data appears in the user interface. + + ### HTTP Endpoint + + `POST /v3/:account_id/shopper_activity/order/batch` + + ### Arguments + + + + + + + + + + + + + + +
KeyDescription
ordersRequired. An Array with between 1 and 1000 objects containing order activity.
+ + ## Create or update a batch of products + + > To create or update a batch of products: + + ```shell + curl -X POST "https://api.getdrip.com/v3/YOUR_ACCOUNT_ID/shopper_activity/product/batch" \ + -H "Content-Type: application/json" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: \ + -d @- << EOF + { + "products": [ + { + "provider": "my_custom_platform", + "action": "created", + "occurred_at": "2019-01-28T12:15:23Z", + "product_id": "B01J4SWO1G", + "product_variant_id": "B01J4SWO1G-CW-BOTT", + "sku": "XHB-1234", + "name": "The Coolest Water Bottle", + "brand": "Drip", + "categories": [ + "Accessories" + ], + "price": 11.16, + "inventory": 42, + "product_url": "https://mysuperstore.com/dp/B01J4SWO1G", + "image_url": "https://www.getdrip.com/images/example_products/water_bottle.png" + }, + { + "provider": "my_custom_platform", + "action": "created", + "occurred_at": "2019-01-28T12:15:23Z", + "product_id": "B01J4SWO1G2", + "product_variant_id": "B01J4SWO1G-CW-BOTT2", + "sku": "XHB-1235", + "name": "The Coolest Water Bottle", + "brand": "Drip", + "categories": [ + "Accessories" + ], + "price": 11.16, + "inventory": 42, + "product_url": "https://mysuperstore.com/dp/B01J4SWO1G2", + "image_url": "https://www.getdrip.com/images/example_products/water_bottle.png" + }, + { + "provider": "my_custom_platform", + "action": "created", + "occurred_at": "2019-01-28T12:15:23Z", + "product_id": "B01J4SWO1G3", + "product_variant_id": "B01J4SWO1G-CW-BOTT3", + "sku": "XHB-1236", + "name": "The Coolest Water Bottle", + "brand": "Drip", + "categories": [ + "Accessories" + ], + "price": 11.16, + "inventory": 42, + "product_url": "https://mysuperstore.com/dp/B01J4SWO1G3", + "image_url": "https://www.getdrip.com/images/example_products/water_bottle.png" + } + ] + } + EOF + ``` + + > Responds with a 202 Accepted if successful. That means the server accepted the request and queued it for processing. The response includes a list of unique request_ids that can be used to check the status of the request later on: + + ```json + { + "request_ids": [ + "db8a7b16-32dd-4863-8b6e-818e3eaab99a", + "002048e9-3692-4f0a-8bbf-80a077acf642" + ] + } + ``` + + + We recommend using this API endpoint when you need to import a collection of products that will likely exceed the regular rate limit of 3,600 requests per hour. + + Note: Since our batch APIs process requests in the background, there may be a delay between the time you submit your + request and the time your data appears in the user interface. + + ### HTTP Endpoint + + `POST /v3/:account_id/shopper_activity/product/batch` + + ### Arguments + + + + + + + + + + + + + + +
KeyDescription
productsRequired. An Array with between 1 and 1000 products containing product activity.
+ +paths: + /{account_id}/subscribers/batches: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + + post: + operationId: createSubscriberBatch + tags: [Batch API] + summary: Create or update a batch of subscribers + x-slate: + intro: "To create or update a batch of subscribers:" + response: + intro: "Responds with a `201 Created` response and an empty JSON response if successful:" + sections: | + We recommend using this API endpoint when you need to create or update a collection of subscribers at once. + + Note: Since our batch APIs process requests in the background, there may be a delay between the time you submit your request and the time your data appears in the user interface. + arguments: | + + + + + + + + + + + + + +
KeyDescription
subscribersRequired. An Array with between 1 and 1000 objects containing subscriber data.
+ description: | + We recommend using this API endpoint when you need to create or update a + collection of subscribers at once. + + Note: Since our batch APIs process requests in the background, there may + be a delay between the time you submit your request and the time your + data appears in the user interface. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [batches] + properties: + batches: + type: array + items: + type: object + required: [subscribers] + properties: + subscribers: + type: array + description: An Array with between 1 and 1000 objects containing subscriber data. + minItems: 1 + maxItems: 1000 + items: + type: object + description: An object containing subscriber data. + properties: + email: + type: string + format: email + time_zone: + type: string + tags: + type: array + items: + type: string + custom_fields: + type: object + example: + batches: + - subscribers: + - email: "john@acme.com" + time_zone: "America/Los_Angeles" + tags: ["Customer", "SEO"] + custom_fields: + shirt_size: "Medium" + - email: "joe@acme.com" + time_zone: "America/Los_Angeles" + tags: ["Prospect"] + custom_fields: + shirt_size: "Medium" + responses: + "201": + description: Created. Responds with an empty JSON response if successful. + content: + application/json: + schema: + type: object + example: {} + "422": + $ref: "../components.yaml#/components/responses/ValidationError" + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl -X POST "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/subscribers/batches" \ + -H "Content-Type: application/json" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: \ + -d @- << EOF + { + "batches": [{ + "subscribers": [ + { + "email": "john@acme.com", + "time_zone": "America/Los_Angeles", + "tags": ["Customer", "SEO"], + "custom_fields": { + "shirt_size": "Medium" + } + }, + { + "email": "joe@acme.com", + "time_zone": "America/Los_Angeles", + "tags": ["Prospect"], + "custom_fields": { + "shirt_size": "Medium" + } + } + ] + }] + } + EOF + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + # An array of subscribers + subscribers = [ + { + "email": "john@acme.com" + }, + { + "email": "jane@acme.com" + } + # ... + ] + + response = client.create_or_update_subscribers(subscribers) + + if response.success? + # ... + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const batch = { + "batches": [{ + "subscribers": [ + { + "email": "john@acme.com", + "tags": "Dog Person" + }, + { + "email": "jane@acme.com", + "tags": "Cat Person" + } + // Lots more subscribers... + ] + }] + }; + + client.updateBatchSubscribers(batch, (errors, responses, bodies) => { + // Do stuff + } + ); + + /{account_id}/unsubscribes/batches: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + + post: + operationId: unsubscribeSubscriberBatch + tags: [Batch API] + summary: Unsubscribe a batch of subscribers + x-slate: + intro: "To globally unsubscribe a batch of subscribers:" + response: + intro: "Responds with a `204 No Content` response if successful." + arguments: | + + + + + + + + + + + + + +
KeyDescription
subscribersRequired. An Array with between 1 and 1000 objects containing subscriber data.
+ requestBody: + required: true + content: + application/json: + schema: + type: object + required: [batches] + properties: + batches: + type: array + items: + type: object + required: [subscribers] + properties: + subscribers: + type: array + description: An Array with between 1 and 1000 objects containing subscriber data. + minItems: 1 + maxItems: 1000 + items: + type: object + description: An object containing subscriber data. + properties: + email: + type: string + format: email + example: + batches: + - subscribers: + - email: "john@acme.com" + - email: "jane@acme.com" + responses: + "204": + description: No Content. The batch of subscribers was unsubscribed. + "422": + $ref: "../components.yaml#/components/responses/ValidationError" + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl -X POST "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/unsubscribes/batches" \ + -H "Content-Type: application/json" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: \ + -d @- << EOF + { + "batches": [{ + "subscribers": [ + { + "email": "john@acme.com" + }, + { + "email": "jane@acme.com" + } + ] + }] + } + EOF + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + # An array of subscribers + subscribers = [ + { + "email": "john@acme.com" + }, + { + "email": "jane@acme.com" + } + # ... + ] + + response = client.unsubscribe_subscribers(subscribers) + + if response.success? + # ... + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const payload = { + batches: + [ + { + subscribers: [ + { + email: "someone@example.com" + } + ] + } + ] + }; + + client.unsubscribeBatchSubscribers(payload) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + + /{account_id}/events/batches: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + + post: + operationId: recordEventBatch + tags: [Batch API] + summary: Record a batch of events + x-slate: + intro: "To record a batch of events:" + response: + intro: "Responds with a `201 Created` response and an empty JSON response if successful:" + sections: | + We recommend using this API endpoint when you need to record a collection of events at once that will likely exceed the regular rate limit of 3,600 requests per hour. + + Note: Since our batch APIs process requests in the background, there may be a delay between the time you submit your request and the time your data appears in the user interface. + arguments: | + + + + + + + + + + + + + +
KeyDescription
eventsRequired. An Array with between 1 and 1000 objects containing event data.
+ description: | + We recommend using this API endpoint when you need to record a collection + of events at once that will likely exceed the regular rate limit of 3,600 + requests per hour. + + Note: Since our batch APIs process requests in the background, there may + be a delay between the time you submit your request and the time your + data appears in the user interface. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [batches] + properties: + batches: + type: array + items: + type: object + required: [events] + properties: + events: + type: array + description: An Array with between 1 and 1000 objects containing event data. + minItems: 1 + maxItems: 1000 + items: + type: object + description: An object containing event data. + properties: + email: + type: string + format: email + action: + type: string + example: + batches: + - events: + - email: "john@acme.com" + action: "Opened a door" + - email: "joe@acme.com" + action: "Closed a door" + responses: + "201": + description: Created. Responds with an empty JSON response if successful. + content: + application/json: + schema: + type: object + example: {} + "422": + $ref: "../components.yaml#/components/responses/ValidationError" + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl -X POST "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/events/batches" \ + -H "Content-Type: application/json" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: \ + -d @- << EOF + { + "batches": [{ + "events": [ + { + "email": "john@acme.com", + "action": "Opened a door" + }, + { + "email": "joe@acme.com", + "action": "Closed a door" + } + ] + }] + } + EOF + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + # An array of events + events = [ + { + "email": "john@acme.com", + "action": "Opened a door" + }, + { + "email": "joe@acme.com", + "action": "Closed a door" + } + # ... + ] + + response = client.track_events(events) + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const payload = [{ + events: [ + { + email: "john@acme.com", + action: "Opened a door" + }, + { + email: "joe@acme.com", + action: "Closed a door" + } + ] + }]; + + client.recordBatchEvents(payload) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + + /{account_id}/orders/batches: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + + post: + operationId: createOrderBatch + tags: [Batch API] + summary: Create or update a batch of orders (Legacy) + x-slate: + intro: "To create or update a batch of orders:" + response: + intro: "Responds with a `202 Accepted` response and an empty JSON response:" + sections: | + + + We recommend using this API endpoint when you need to create a collection of orders and subscribers at once that will likely exceed the regular rate limit of 3,600 requests per hour. + + Note: Since our batch APIs process requests in the background, there may be a delay between the time you submit your request and the time your data appears in the user interface. + arguments: | + + + + + + + + + + + + + +
KeyDescription
ordersRequired. An Array with between 1 and 1000 objects containing order data.
+ description: | + We've released a new and improved batch endpoint for Orders functionality! + Note that the legacy Orders endpoint will continue to function. We will + give fair notice before we retire it in the future. + + We recommend using this API endpoint when you need to create a collection + of orders and subscribers at once that will likely exceed the regular rate + limit of 3,600 requests per hour. + + Note: Since our batch APIs process requests in the background, there may + be a delay between the time you submit your request and the time your + data appears in the user interface. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [batches] + properties: + batches: + type: array + items: + type: object + required: [orders] + properties: + orders: + type: array + description: An Array with between 1 and 1000 objects containing order data. + minItems: 1 + maxItems: 1000 + items: + type: object + description: An object containing order data. + example: + batches: + - orders: + - email: "john@acme.com" + provider: "shopify" + upstream_id: "abcdef" + identifier: "Order_123456" + amount: 4900 + tax: 100 + fees: 0 + discount: 0 + permalink: "http://myorders.com/orders/123456" + currency_code: "USD" + properties: + shirt_size: "Medium" + color: "red" + occurred_at: "2013-06-21T10:31:58Z" + closed_at: "2013-06-21T10:35:58Z" + cancelled_at: null + financial_state: "paid" + fulfillment_state: "fulfilled" + billing_address: + name: "Bill Billington" + first_name: "Bill" + last_name: "Billington" + company: "Bills R US" + address_1: "123 Bill St." + address_2: "Apt. B" + city: "Billtown" + state: "CA" + zip: "01234" + country: "United States" + phone: "555-555-5555" + email: "bill@bills.com" + shipping_address: + name: "Ship Shippington" + first_name: "Ship" + last_name: "Shipington" + company: "Shipping 4 Less" + address_1: "123 Ship St." + address_2: "null" + city: "Shipville" + state: "CA" + zip: "01234" + country: "United States" + phone: "555-555-5555" + email: "ship@shipping.com" + items: + - id: "8888888" + product_id: "765432" + sku: "4444" + amount: 4900 + name: "Canoe" + quantity: 1 + upstream_id: "hijkl" + upstream_product_id: "opqrs" + upstream_product_variant_id: "zyxwv" + price: 4900 + tax: 100 + fees: 0 + discount: 100 + taxable: true + properties: + color: "black" + - email: "joe@acme.com" + provider: "shopify" + upstream_id: "fedcba" + identifier: "Order_654321" + amount: 4900 + tax: 100 + fees: 0 + discount: 0 + permalink: "http://myorders.com/orders/654321" + currency_code: "USD" + properties: + size: "small" + color: "blue" + occurred_at: "2013-05-18T10:31:58Z" + closed_at: "2013-05-18T10:35:58Z" + cancelled_at: null + financial_state: "paid" + fulfillment_state: "fulfilled" + billing_address: + name: "Bill Billington" + first_name: "Bill" + last_name: "Billington" + company: "Bills R US" + address_1: "123 Bill St." + address_2: "Apt. B" + city: "Billtown" + state: "CA" + zip: "01234" + country: "United States" + phone: "555-555-5555" + email: "bill@bills.com" + shipping_address: + name: "Ship Shippington" + first_name: "Ship" + last_name: "Shipington" + company: "Shipping 4 Less" + address_1: "123 Ship St." + address_2: "null" + city: "Shipville" + state: "CA" + zip: "01234" + country: "United States" + phone: "555-555-5555" + email: "ship@shipping.com" + items: + - id: "8888888" + product_id: "765432" + sku: "4444" + amount: 4900 + name: "Canoe" + quantity: 1 + upstream_id: "hijkl" + upstream_product_id: "opqrs" + upstream_product_variant_id: "zyxwv" + price: 4900 + tax: 100 + fees: 0 + discount: 100 + taxable: true + properties: + color: "black" + responses: + "202": + description: Accepted. Responds with an empty JSON response. + content: + application/json: + schema: + type: object + example: {} + "422": + $ref: "../components.yaml#/components/responses/ValidationError" + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl -X POST "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/orders/batches" \ + -H "Content-Type: application/json" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: \ + -d @- << EOF + { + "batches": [{ + "orders": [ + { + "email": "john@acme.com", + "provider": "shopify", + "upstream_id": "abcdef", + "identifier": "Order_123456", + "amount": 4900, + "tax": 100, + "fees": 0, + "discount": 0, + "permalink": "http://myorders.com/orders/123456", + "currency_code": "USD", + "properties": { + "shirt_size": "Medium", + "color": "red" + }, + "occurred_at": "2013-06-21T10:31:58Z", + "closed_at": "2013-06-21T10:35:58Z", + "cancelled_at": null, + "financial_state": "paid", + "fulfillment_state": "fulfilled", + "billing_address": { + "name": "Bill Billington", + "first_name": "Bill", + "last_name": "Billington", + "company": "Bills R US", + "address_1": "123 Bill St.", + "address_2": "Apt. B", + "city": "Billtown", + "state": "CA", + "zip": "01234", + "country": "United States", + "phone": "555-555-5555", + "email": "bill@bills.com" + }, + "shipping_address": { + "name": "Ship Shippington", + "first_name": "Ship", + "last_name": "Shipington", + "company": "Shipping 4 Less", + "address_1": "123 Ship St.", + "address_2": "null", + "city": "Shipville", + "state": "CA", + "zip": "01234", + "country": "United States", + "phone": "555-555-5555", + "email": "ship@shipping.com" + }, + "items": [{ + "id": "8888888", + "product_id": "765432", + "sku": "4444", + "amount": 4900, + "name": "Canoe", + "quantity": 1, + "upstream_id": "hijkl", + "upstream_product_id": "opqrs", + "upstream_product_variant_id": "zyxwv", + "price": 4900, + "tax": 100, + "fees": 0, + "discount": 100, + "taxable": true, + "properties": { + "color": "black" + } + }] + }, + { + "email": "joe@acme.com", + "provider": "shopify", + "upstream_id": "fedcba", + "identifier": "Order_654321", + "amount": 4900, + "tax": 100, + "fees": 0, + "discount": 0, + "permalink": "http://myorders.com/orders/654321", + "currency_code": "USD", + "properties": { + "size": "small", + "color": "blue" + }, + "occurred_at": "2013-05-18T10:31:58Z", + "closed_at": "2013-05-18T10:35:58Z", + "cancelled_at": null, + "financial_state": "paid", + "fulfillment_state": "fulfilled", + "billing_address": { + "name": "Bill Billington", + "first_name": "Bill", + "last_name": "Billington", + "company": "Bills R US", + "address_1": "123 Bill St.", + "address_2": "Apt. B", + "city": "Billtown", + "state": "CA", + "zip": "01234", + "country": "United States", + "phone": "555-555-5555", + "email": "bill@bills.com" + }, + "shipping_address": { + "name": "Ship Shippington", + "first_name": "Ship", + "last_name": "Shipington", + "company": "Shipping 4 Less", + "address_1": "123 Ship St.", + "address_2": "null", + "city": "Shipville", + "state": "CA", + "zip": "01234", + "country": "United States", + "phone": "555-555-5555", + "email": "ship@shipping.com" + }, + "items": [{ + "id": "8888888", + "product_id": "765432", + "sku": "4444", + "amount": 4900, + "name": "Canoe", + "quantity": 1, + "upstream_id": "hijkl", + "upstream_product_id": "opqrs", + "upstream_product_variant_id": "zyxwv", + "price": 4900, + "tax": 100, + "fees": 0, + "discount": 100, + "taxable": true, + "properties": { + "color": "black" + } + }] + } + ] + }] + } + EOF diff --git a/spec/fragments/broadcasts.yaml b/spec/fragments/broadcasts.yaml new file mode 100644 index 00000000000..0dc3d0deedb --- /dev/null +++ b/spec/fragments/broadcasts.yaml @@ -0,0 +1,1217 @@ +tags: + - name: Broadcasts + description: Single-Email Campaigns (Broadcasts) are one-time emails sent to a segment of your subscribers. + x-slate: + title: Single-Email Campaigns
(aka Broadcasts) + represent: + - intro: "Single-Email Campaigns are represented as follows:" + schema: Broadcast + - intro: "All responses containing Single-Email Campaign data also include the following top-level link data:" + json: | + { + "links": { + "broadcasts.account": "https://api.getdrip.com/v2/accounts/{broadcasts.account}" + } + } + properties: Broadcast + lead_extra: | + ## Email Content + + > The content object structure: + + ```json + { + "content": { + "html": { + "type": "document", + "value": "

Hello!

" + }, + "text": { + "type": "document", + "value": "Hello!\n\nWelcome to our newsletter." + } + } + } + ``` + + Email content is provided via a `content` object containing content type keys. Each key maps + to an object with a `type` (the content variant) and a `value` (the content itself). + + + + + + + + + + + + + + + + + + +
KeyDescription
htmlRequired when creating. The HTML email content. Currently the only supported type is document: a full HTML document with a complete <html> structure. Supports Liquid templating.
textOptional. The plain text version of the email. Currently the only supported type is document: a full plain text document. Supports Liquid templating. If omitted, the plain text version is generated automatically from the HTML.
+ + HTML content is validated and sanitized for security: JavaScript is not allowed, and dangerous + CSS patterns are blocked. + +paths: + /{account_id}/broadcasts: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + + get: + operationId: listBroadcasts + tags: [Broadcasts] + summary: List all Single-Email Campaigns + x-slate: + intro: "To list Single-Email Campaigns in an account:" + response: + intro: "The response looks like this:" + arguments: auto + parameters: + - name: page + in: query + description: The page number (1-indexed). Defaults to `1`. + schema: + type: integer + minimum: 1 + default: 1 + - name: per_page + in: query + description: The number of results per page, up to a maximum of `100`. Defaults to `100`. + schema: + type: integer + maximum: 100 + default: 100 + - name: status + in: query + description: >- + Filter by one of the following statuses: `draft`, `scheduled`, + `sending`, `sent`, `canceled`, `deleted`, or `all`. By default, + deleted Single-Email Campaigns are excluded; use `status=deleted` + or `status=all` to include them. + schema: + type: string + enum: [draft, scheduled, sending, sent, canceled, deleted, all] + - name: sort + in: query + description: >- + Sort results by one of these fields: `created_at`, `updated_at`, + `send_at`, or `name`. Defaults to `created_at`. + schema: + type: string + enum: [created_at, updated_at, send_at, name] + default: created_at + - name: direction + in: query + description: "Filter sort direction with: `asc` or `desc`. Defaults to `asc`." + schema: + type: string + enum: [asc, desc] + default: asc + responses: + "200": + description: >- + A paginated list of Single-Email Campaigns. The `broadcasts` + property is an array of broadcast objects. + content: + application/json: + schema: + type: object + required: [broadcasts] + properties: + links: + type: object + description: Top-level link data. + properties: + "broadcasts.account": + type: string + meta: + type: object + description: Pagination metadata. + properties: + page: + type: integer + sort: + type: string + direction: + type: string + count: + type: integer + total_pages: + type: integer + total_count: + type: integer + status: + type: string + broadcasts: + type: array + items: + $ref: "#/components/schemas/Broadcast" + example: + links: + broadcasts.account: "https://api.getdrip.com/v2/accounts/{broadcasts.account}" + meta: + page: 1 + sort: created_at + direction: asc + count: 5 + total_pages: 1 + total_count: 5 + status: all + broadcasts: + - id: "123456" + status: sent + name: "4 Marketing Automation Trends for 2015" + from_name: John Doe + from_email: john@example.com + postal_address: "123 Anywhere St\nFresno, CA 99999" + localize_sending_time: true + send_at: "2015-07-01T10:00:00Z" + bcc: null + created_at: "2015-06-21T10:31:58Z" + href: "https://api.getdrip.com/v2/9999999/broadcast/123456" + preview_url: "https://www.getdrip.com/broadcasts/123456/2d83a64861f23b1c35a3b8d6ee3b54f7" + subject: "4 Marketing Automation Trends for 2015" + html_body: HTML body + text_body: Text body + links: + account: "9999999" + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/broadcasts" \ + -H "Content-Type: application/json" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + response = client.broadcasts + + if response.success? + puts response.body["broadcasts"] + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const options = { status: "sent" }; + + client.listBroadcasts(options) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + + post: + operationId: createBroadcast + tags: [Broadcasts] + summary: Create a Single-Email Campaign + description: >- + Single-Email Campaigns created via the API start in the `draft` status. + Scheduling and sending are then managed through the Drip UI. + x-slate: + intro: "To create a Single-Email Campaign:" + response: + - intro: "Responds with a `201 Created` and the new Single-Email Campaign if successful. The `Location` header contains the URL of the created record:" + - intro: "If the request is invalid, responds with a `422 Unprocessable Entity`:" + status: "422" + show-description: true + arguments: auto + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [broadcasts] + properties: + broadcasts: + type: array + items: + $ref: "#/components/schemas/NewBroadcast" + example: + broadcasts: + - name: December Newsletter + subject: "Your December Newsletter is here!" + preheader: "See what's new this month..." + content: + html: + type: document + value: "

Hello!

" + text: + type: document + value: "Hello! Welcome to our newsletter." + postal_address: "123 Main St, Minneapolis, MN 55401" + responses: + "201": + description: >- + Created. Responds with the new Single-Email Campaign. The + `Location` header contains the URL of the created record. + content: + application/json: + schema: + type: object + required: [broadcasts] + properties: + links: + type: object + description: Top-level link data. + properties: + "broadcasts.account": + type: string + broadcasts: + type: array + description: An array containing the new broadcast object. + items: + $ref: "#/components/schemas/Broadcast" + example: + links: + broadcasts.account: "https://api.getdrip.com/v2/accounts/{broadcasts.account}" + broadcasts: + - id: "123456" + status: draft + name: "4 Marketing Automation Trends for 2015" + from_name: John Doe + from_email: john@example.com + postal_address: "123 Anywhere St\nFresno, CA 99999" + localize_sending_time: true + send_at: "2015-07-01T10:00:00Z" + bcc: null + created_at: "2015-06-21T10:31:58Z" + href: "https://api.getdrip.com/v2/9999999/broadcast/123456" + preview_url: "https://www.getdrip.com/broadcasts/123456/2d83a64861f23b1c35a3b8d6ee3b54f7" + subject: "4 Marketing Automation Trends for 2015" + html_body: HTML body + text_body: Text body + links: + account: "9999999" + "422": + $ref: "../components.yaml#/components/responses/ValidationError" + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl -X POST "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/broadcasts" \ + -H "Content-Type: application/json" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: \ + -d @- << EOF + { + "broadcasts": [{ + "name": "December Newsletter", + "subject": "Your December Newsletter is here!", + "preheader": "See what's new this month...", + "content": { + "html": { + "type": "document", + "value": "

Hello!

" + }, + "text": { + "type": "document", + "value": "Hello! Welcome to our newsletter." + } + }, + "postal_address": "123 Main St, Minneapolis, MN 55401" + }] + } + EOF + - lang: ruby + label: Ruby + source: | + require "net/http" + require "json" + + uri = URI("https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/broadcasts") + + request = Net::HTTP::Post.new(uri) + request.basic_auth("YOUR_API_KEY", "") + request["User-Agent"] = "Your App Name (www.yourapp.com)" + request["Content-Type"] = "application/json" + request.body = { + broadcasts: [{ + name: "December Newsletter", + subject: "Your December Newsletter is here!", + preheader: "See what's new this month...", + content: { + html: { + type: "document", + value: "

Hello!

" + }, + text: { + type: "document", + value: "Hello! Welcome to our newsletter." + } + }, + postal_address: "123 Main St, Minneapolis, MN 55401" + }] + }.to_json + + response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http| + http.request(request) + end + + puts response.body + - lang: javascript + label: JavaScript + source: | + const response = await fetch( + "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/broadcasts", + { + method: "POST", + headers: { + "Content-Type": "application/json", + "User-Agent": "Your App Name (www.yourapp.com)", + "Authorization": "Basic " + Buffer.from("YOUR_API_KEY:").toString("base64") + }, + body: JSON.stringify({ + broadcasts: [{ + name: "December Newsletter", + subject: "Your December Newsletter is here!", + preheader: "See what's new this month...", + content: { + html: { + type: "document", + value: "

Hello!

" + }, + text: { + type: "document", + value: "Hello! Welcome to our newsletter." + } + }, + postal_address: "123 Main St, Minneapolis, MN 55401" + }] + }) + } + ); + + const body = await response.json(); + + /{account_id}/broadcasts/{broadcast_id}: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + - name: broadcast_id + in: path + required: true + description: The ID of the Single-Email Campaign. + schema: + type: string + + get: + operationId: fetchBroadcast + tags: [Broadcasts] + summary: Fetch a Single-Email Campaign + x-slate: + intro: "To fetch a specific Single-Email Campaign:" + response: + intro: "The response looks like this:" + arguments: none + responses: + "200": + description: >- + The requested Single-Email Campaign. The `broadcasts` property is + an array of one broadcast object. + content: + application/json: + schema: + type: object + required: [broadcasts] + properties: + links: + type: object + description: Top-level link data. + properties: + "broadcasts.account": + type: string + broadcasts: + type: array + description: An array of one broadcast object. + items: + $ref: "#/components/schemas/Broadcast" + example: + links: + broadcasts.account: "https://api.getdrip.com/v2/accounts/{broadcasts.account}" + broadcasts: + - id: "123456" + status: sent + name: "4 Marketing Automation Trends for 2015" + from_name: John Doe + from_email: john@example.com + postal_address: "123 Anywhere St\nFresno, CA 99999" + localize_sending_time: true + send_at: "2015-07-01T10:00:00Z" + bcc: null + created_at: "2015-06-21T10:31:58Z" + href: "https://api.getdrip.com/v2/9999999/broadcast/123456" + preview_url: "https://www.getdrip.com/broadcasts/123456/2d83a64861f23b1c35a3b8d6ee3b54f7" + subject: "4 Marketing Automation Trends for 2015" + html_body: HTML body + text_body: Text body + links: + account: "9999999" + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/broadcasts/BROADCAST_ID" \ + -H "Content-Type: application/json" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + broadcast_id = "BROADCAST_ID" + response = client.broadcast(broadcast_id) + + if response.success? + puts response.body + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const broadcastId = "BROADCAST_ID"; + + client.fetchBroadcast(broadcastId) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + + patch: + operationId: updateBroadcast + tags: [Broadcasts] + summary: Update a Single-Email Campaign + description: >- + Only Single-Email Campaigns in the `draft` status can be updated via + the API; all other statuses are read-only. Updates are partial: only + the fields you include are changed. + + + Status, scheduling (`send_at`, `localize_sending_time`), and recipient + segmentation cannot be updated via the API — they are managed through + the Drip UI. + x-slate: + intro: "To update a Single-Email Campaign:" + response: + - intro: "Responds with a `200 OK` and the updated Single-Email Campaign if successful:" + - intro: "If the Single-Email Campaign is not a draft, responds with a `409 Conflict`:" + status: "409" + show-description: true + arguments: auto + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [broadcasts] + properties: + broadcasts: + type: array + items: + $ref: "#/components/schemas/BroadcastUpdate" + example: + broadcasts: + - subject: "Updated: December Newsletter!" + preheader: "New content inside..." + content: + html: + type: document + value: "

Updated content!

" + responses: + "200": + description: OK. The updated Single-Email Campaign. + content: + application/json: + schema: + type: object + required: [broadcasts] + properties: + links: + type: object + description: Top-level link data. + properties: + "broadcasts.account": + type: string + broadcasts: + type: array + description: An array of one broadcast object. + items: + $ref: "#/components/schemas/Broadcast" + example: + links: + broadcasts.account: "https://api.getdrip.com/v2/accounts/{broadcasts.account}" + broadcasts: + - id: "123456" + status: draft + name: "4 Marketing Automation Trends for 2015" + from_name: John Doe + from_email: john@example.com + postal_address: "123 Anywhere St\nFresno, CA 99999" + localize_sending_time: true + send_at: "2015-07-01T10:00:00Z" + bcc: null + created_at: "2015-06-21T10:31:58Z" + href: "https://api.getdrip.com/v2/9999999/broadcast/123456" + preview_url: "https://www.getdrip.com/broadcasts/123456/2d83a64861f23b1c35a3b8d6ee3b54f7" + subject: "Updated: December Newsletter!" + html_body: HTML body + text_body: Text body + links: + account: "9999999" + "409": + description: Conflict. The Single-Email Campaign is not a draft. + content: + application/json: + schema: + $ref: "../components.yaml#/components/schemas/ErrorResponse" + example: + errors: + - code: conflict_error + message: Cannot update a broadcast with status 'sent' + "422": + $ref: "../components.yaml#/components/responses/ValidationError" + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl -X PATCH "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/broadcasts/BROADCAST_ID" \ + -H "Content-Type: application/json" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: \ + -d @- << EOF + { + "broadcasts": [{ + "subject": "Updated: December Newsletter!", + "preheader": "New content inside...", + "content": { + "html": { + "type": "document", + "value": "

Updated content!

" + } + } + }] + } + EOF + - lang: ruby + label: Ruby + source: | + require "net/http" + require "json" + + uri = URI("https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/broadcasts/BROADCAST_ID") + + request = Net::HTTP::Patch.new(uri) + request.basic_auth("YOUR_API_KEY", "") + request["User-Agent"] = "Your App Name (www.yourapp.com)" + request["Content-Type"] = "application/json" + request.body = { + broadcasts: [{ + subject: "Updated: December Newsletter!", + preheader: "New content inside...", + content: { + html: { + type: "document", + value: "

Updated content!

" + } + } + }] + }.to_json + + response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http| + http.request(request) + end + + puts response.body + - lang: javascript + label: JavaScript + source: | + const response = await fetch( + "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/broadcasts/BROADCAST_ID", + { + method: "PATCH", + headers: { + "Content-Type": "application/json", + "User-Agent": "Your App Name (www.yourapp.com)", + "Authorization": "Basic " + Buffer.from("YOUR_API_KEY:").toString("base64") + }, + body: JSON.stringify({ + broadcasts: [{ + subject: "Updated: December Newsletter!", + preheader: "New content inside...", + content: { + html: { + type: "document", + value: "

Updated content!

" + } + } + }] + }) + } + ); + + const body = await response.json(); + + delete: + operationId: deleteBroadcast + tags: [Broadcasts] + summary: Delete a Single-Email Campaign + description: >- + Deleting is a soft delete: the Single-Email Campaign transitions to the + `deleted` status and is hidden from default listings, but historical + data is preserved. Single-Email Campaigns in the `sending` status + cannot be deleted. + x-slate: + intro: "To delete a Single-Email Campaign:" + response: + - intro: "Responds with a `200 OK` and the Single-Email Campaign in the `deleted` status:" + - intro: "If the Single-Email Campaign is currently sending, responds with a `409 Conflict`:" + status: "409" + show-description: true + arguments: none + responses: + "200": + description: OK. The Single-Email Campaign in the `deleted` status. + content: + application/json: + schema: + type: object + required: [broadcasts] + properties: + links: + type: object + description: Top-level link data. + properties: + "broadcasts.account": + type: string + broadcasts: + type: array + description: An array of one broadcast object. + items: + $ref: "#/components/schemas/Broadcast" + example: + links: + broadcasts.account: "https://api.getdrip.com/v2/accounts/{broadcasts.account}" + broadcasts: + - id: "123456" + status: deleted + name: "4 Marketing Automation Trends for 2015" + from_name: John Doe + from_email: john@example.com + postal_address: "123 Anywhere St\nFresno, CA 99999" + localize_sending_time: true + send_at: "2015-07-01T10:00:00Z" + bcc: null + created_at: "2015-06-21T10:31:58Z" + href: "https://api.getdrip.com/v2/9999999/broadcast/123456" + preview_url: null + subject: "4 Marketing Automation Trends for 2015" + html_body: HTML body + text_body: Text body + links: + account: "9999999" + "409": + description: Conflict. The Single-Email Campaign is currently sending. + content: + application/json: + schema: + $ref: "../components.yaml#/components/schemas/ErrorResponse" + example: + errors: + - code: conflict_error + message: Cannot delete a broadcast that is currently sending + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl -X DELETE "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/broadcasts/BROADCAST_ID" \ + -H "Content-Type: application/json" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: ruby + label: Ruby + source: | + require "net/http" + + uri = URI("https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/broadcasts/BROADCAST_ID") + + request = Net::HTTP::Delete.new(uri) + request.basic_auth("YOUR_API_KEY", "") + request["User-Agent"] = "Your App Name (www.yourapp.com)" + + response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http| + http.request(request) + end + + puts response.body + - lang: javascript + label: JavaScript + source: | + const response = await fetch( + "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/broadcasts/BROADCAST_ID", + { + method: "DELETE", + headers: { + "User-Agent": "Your App Name (www.yourapp.com)", + "Authorization": "Basic " + Buffer.from("YOUR_API_KEY:").toString("base64") + } + } + ); + + const body = await response.json(); + + /{account_id}/broadcasts/{broadcast_id}/send_test: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + - name: broadcast_id + in: path + required: true + description: The ID of the Single-Email Campaign. + schema: + type: string + + post: + operationId: sendBroadcastTestEmail + tags: [Broadcasts] + summary: Send a test email + description: |- + Sends a test email so you can preview how the Single-Email Campaign will appear to + recipients. Test emails are marked with a "[TEST]" prefix in the from name, and any + links that would trigger automations are stripped for safety. If sending is blocked + on the account (for example, during a trial), tests are only delivered to the + authenticated user. + + ### Recipient validation + + Each recipient email address must satisfy **one** of the following requirements: + + 1. **Verified domain**: the email's domain is a verified sending domain in the account + (e.g., if `example.com` is verified, `anyone@example.com` is allowed). + 2. **Active account member**: the email belongs to an active member of the account. + + Recipients that do not meet either requirement are rejected with a `422` validation error. + + ### Rate limits + + Rate limits are applied per user. + + | Limit | Window | + | --- | --- | + | 5 recipients | Per request | + | 20 requests | Per hour | + | 40 requests | Per day | + x-slate: + intro: "To send a test email for a Single-Email Campaign:" + response: + - intro: "Responds with a `204 No Content` if successful." + - intro: "If a recipient is not allowed, responds with a `422 Unprocessable Entity`:" + status: "422" + - intro: "If a rate limit is exceeded, responds with a `429 Too Many Requests` and a `Retry-After` header indicating when the limit resets:" + status: "429" + sections: | + Sends a test email so you can preview how the Single-Email Campaign will appear to + recipients. Test emails are marked with a "[TEST]" prefix in the from name, and any + links that would trigger automations are stripped for safety. If sending is blocked + on the account (for example, during a trial), tests are only delivered to the + authenticated user. + + ### Recipient validation + + Each recipient email address must satisfy **one** of the following requirements: + + 1. **Verified domain**: the email's domain is a verified sending domain in the account + (e.g., if `example.com` is verified, `anyone@example.com` is allowed). + 2. **Active account member**: the email belongs to an active member of the account. + + Recipients that do not meet either requirement are rejected with a `422` validation error. + + ### Rate limits + + Rate limits are applied per user. + + + + + + + + + + + + + + + + + + + + + + +
LimitWindow
5 recipientsPer request
20 requestsPer hour
40 requestsPer day
+ arguments: auto + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [to_emails] + properties: + to_emails: + type: array + description: >- + An array of 1 to 5 email addresses to send the test to. + See recipient validation above. + minItems: 1 + maxItems: 5 + items: + type: string + format: email + preview_as: + type: string + format: email + description: >- + The email address of an existing subscriber to use for + Liquid template rendering. The test email is personalized + as if sent to this subscriber, using their custom fields + and tags. If omitted, Liquid variables use placeholder + values. + example: + to_emails: + - team@yourverifieddomain.com + preview_as: subscriber@example.com + responses: + "204": + description: No Content. The test email was sent. + "422": + description: >- + Unprocessable Entity. A recipient is not a verified domain or + active account member. + content: + application/json: + schema: + $ref: "../components.yaml#/components/schemas/ErrorResponse" + example: + errors: + - code: validation_error + attribute: to_emails + message: test@unknown-domain.com is not a verified domain or active account member + "429": + description: >- + Too Many Requests. A rate limit was exceeded; the `Retry-After` + header indicates when the limit resets. + content: + application/json: + schema: + $ref: "../components.yaml#/components/schemas/ErrorResponse" + example: + errors: + - code: rate_limit_error + message: Hourly test email limit exceeded. Maximum 20 requests per hour. + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl -X POST "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/broadcasts/BROADCAST_ID/send_test" \ + -H "Content-Type: application/json" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: \ + -d @- << EOF + { + "to_emails": ["team@yourverifieddomain.com"], + "preview_as": "subscriber@example.com" + } + EOF + - lang: ruby + label: Ruby + source: | + require "net/http" + require "json" + + uri = URI("https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/broadcasts/BROADCAST_ID/send_test") + + request = Net::HTTP::Post.new(uri) + request.basic_auth("YOUR_API_KEY", "") + request["User-Agent"] = "Your App Name (www.yourapp.com)" + request["Content-Type"] = "application/json" + request.body = { + to_emails: ["team@yourverifieddomain.com"], + preview_as: "subscriber@example.com" + }.to_json + + response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http| + http.request(request) + end + + puts response.code + - lang: javascript + label: JavaScript + source: | + const response = await fetch( + "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/broadcasts/BROADCAST_ID/send_test", + { + method: "POST", + headers: { + "Content-Type": "application/json", + "User-Agent": "Your App Name (www.yourapp.com)", + "Authorization": "Basic " + Buffer.from("YOUR_API_KEY:").toString("base64") + }, + body: JSON.stringify({ + to_emails: ["team@yourverifieddomain.com"], + preview_as: "subscriber@example.com" + }) + } + ); + + // 204 No Content on success + +components: + schemas: + Broadcast: + type: object + description: >- + A Single-Email Campaign (Broadcast) — a one-time email sent to a + segment of your subscribers. Scheduling (`send_at`, + `localize_sending_time`) and recipient segmentation are managed + through the Drip UI only; these fields are returned as read-only + values in API responses and cannot be set via the API. + example: + id: "123456" + status: sent + name: "4 Marketing Automation Trends for 2015" + from_name: John Doe + from_email: john@example.com + postal_address: "123 Anywhere St\nFresno, CA 99999" + localize_sending_time: true + send_at: "2015-07-01T10:00:00Z" + bcc: null + created_at: "2015-06-21T10:31:58Z" + href: "https://api.getdrip.com/v2/9999999/broadcast/123456" + preview_url: "https://www.getdrip.com/broadcasts/123456/2d83a64861f23b1c35a3b8d6ee3b54f7" + subject: "4 Marketing Automation Trends for 2015" + html_body: HTML body + text_body: Text body + links: + account: "9999999" + properties: + id: + type: string + description: >- + A read-only Drip generated unique id used to identify each + Single-Email Campaign record. + status: + type: string + enum: [draft, scheduled, sending, sent, canceled, deleted] + description: >- + Returns whether the Single-Email Campaign is draft, canceled, + scheduled, sent or sending. Read-only via the API and managed + through the Drip UI. Drafts are the only Single-Email Campaigns + that can be edited via the API; `sending` campaigns cannot be + deleted; `deleted` campaigns are hidden from default listings. + name: + type: string + description: The private name given to the Single-Email Campaign. + from_name: + type: string + description: >- + A "from name" that appears in your sent emails and can be changed + on a per email basis. This setting overrides the account's default + from name. + from_email: + type: string + description: >- + A "from email" that appears in your sent emails and can be changed + on a per email basis. This setting overrides the account's default + from email. + postal_address: + type: string + description: >- + As required by the [CAN-SPAM Act](http://1.usa.gov/YgrzFP), this + is a postal address used for all sent emails and can be changed on + a per email basis. + localize_sending_time: + type: boolean + description: >- + The scheduled send_at time if set to be sent in the subscriber's + time zone. + send_at: + type: ["string", "null"] + format: date-time + description: >- + The timestamp representing when the Single-Email Campaign will be + delivered. + bcc: + type: ["array", "null"] + items: + type: string + description: >- + A list of emails designated to receive a blind copy of the + Single-Email Campaign. + created_at: + type: string + format: date-time + description: >- + A read-only Drip generated timestamp for when the Single-Email + Campaign was first created. + href: + type: string + description: >- + The url designated for retrieving the record via the REST API. + preview_url: + type: ["string", "null"] + description: >- + A read-only public URL for previewing the email content. Does not + require authentication. Returns `null` for deleted Single-Email + Campaigns. + subject: + type: string + description: The Single-Email Campaign's subject. + html_body: + type: string + description: The HTML content used in the email's body. + text_body: + type: string + description: The plain text content used in the email's body. + links: + type: object + description: An object containing the account's REST API URL. + properties: + account: + type: string + + EmailContent: + type: object + description: >- + Email content, containing content type keys. Each key maps to an + object with a `type` (the content variant) and a `value` (the content + itself). HTML content is validated and sanitized for security: + JavaScript is not allowed, and dangerous CSS patterns are blocked. + properties: + html: + type: object + description: >- + Required when creating. The HTML email content. Currently the only + supported `type` is `document`: a full HTML document with a + complete `` structure. Supports Liquid templating. + required: [type, value] + properties: + type: + type: string + enum: [document] + description: The content variant. + value: + type: string + description: The content itself. + text: + type: object + description: >- + Optional. The plain text version of the email. Currently the only + supported `type` is `document`: a full plain text document. + Supports Liquid templating. If omitted, the plain text version is + generated automatically from the HTML. + required: [type, value] + properties: + type: + type: string + enum: [document] + description: The content variant. + value: + type: string + description: The content itself. + + NewBroadcast: + type: object + required: [name, subject, content] + properties: + name: + type: string + maxLength: 255 + description: >- + The private name given to the Single-Email Campaign (255 + characters maximum). + subject: + type: string + description: The email subject line. Supports Liquid templating. + content: + $ref: "#/components/schemas/EmailContent" + description: >- + An Email Content object. At minimum, include an `html` key. + Optionally include a `text` key for the plain text version; if + omitted, the plain text version is generated from the HTML. + preheader: + type: string + description: >- + Preview text shown in email clients before the email is opened. + Supports Liquid templating. + postal_address: + type: string + description: >- + A physical mailing address for CAN-SPAM compliance. Defaults to + the account's postal address. + + BroadcastUpdate: + type: object + properties: + name: + type: string + description: The private name given to the Single-Email Campaign. + subject: + type: string + description: The email subject line. Supports Liquid templating. + content: + $ref: "#/components/schemas/EmailContent" + description: >- + An Email Content object with `html` and/or `text` keys. + preheader: + type: string + description: >- + Preview text shown in email clients before the email is opened. + Supports Liquid templating. + postal_address: + type: string + description: A physical mailing address for CAN-SPAM compliance. diff --git a/spec/fragments/campaigns.yaml b/spec/fragments/campaigns.yaml new file mode 100644 index 00000000000..ab68c768c9a --- /dev/null +++ b/spec/fragments/campaigns.yaml @@ -0,0 +1,959 @@ +tags: + - name: Campaigns + description: Email Series Campaigns in an account. + x-slate: + title: Email Series Campaigns + represent: + - intro: "Email Series Campaigns are represented as follows:" + schema: Campaign + - intro: "All responses containing Email Series Campaign data also include the following top-level link data:" + json: | + { + "links": { + "campaigns.account": "https://api.getdrip.com/v2/accounts/{campaigns.account}", + "campaigns.form": "https://api.getdrip.com/v2/{campaigns.account}/forms/{campaigns.forms}", + "campaigns.subscribers": "https://api.getdrip.com/v2/{campaigns.account}/campaigns/{campaigns.id}/subscribers" + } + } + properties: Campaign + +paths: + /{account_id}/campaigns: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + + get: + operationId: listCampaigns + tags: [Campaigns] + summary: List all Email Series Campaigns + x-slate: + intro: "To list all Email Series Campaigns:" + response: + intro: "The response looks like this:" + arguments: auto + parameters: + - name: status + in: query + description: "Optional. Filter by one of the following statuses: `draft`, `active`, or `paused`. Defaults to `all`." + schema: + type: string + enum: [all, draft, active, paused] + default: all + - name: sort + in: query + description: "Optional. Sort results by one of these fields: `created_at` or `name`. Defaults to `created_at`." + schema: + type: string + enum: [created_at, name] + default: created_at + - name: direction + in: query + description: "Optional. Filter sort direction with: `asc` or `desc`. Defaults to `asc`." + schema: + type: string + enum: [asc, desc] + default: asc + responses: + "200": + description: A list of Email Series Campaigns. The `campaigns` property is an array of campaign objects. + content: + application/json: + schema: + type: object + properties: + links: + type: object + description: Top-level link data included with all responses containing Email Series Campaign data. + meta: + type: object + properties: + page: + type: integer + sort: + type: string + direction: + type: string + count: + type: integer + total_pages: + type: integer + total_count: + type: integer + status: + type: string + campaigns: + type: array + items: + $ref: "#/components/schemas/Campaign" + example: + links: + campaigns.account: "https://api.getdrip.com/v2/accounts/{campaigns.account}" + campaigns.form: "https://api.getdrip.com/v2/{campaigns.account}/forms/{campaigns.forms}" + campaigns.subscribers: "https://api.getdrip.com/v2/{campaigns.account}/campaigns/{campaigns.id}/subscribers" + meta: + page: 1 + sort: created_at + direction: asc + count: 5 + total_pages: 1 + total_count: 5 + status: all + campaigns: + - id: "123456" + status: active + name: SEO Email Course + from_name: John Doe + from_email: john@example.com + postal_address: "123 Anywhere St\nFresno, CA 99999" + minutes_from_midnight: 440 + localize_sending_time: true + days_of_the_week_mask: "0111110" + start_immediately: true + double_optin: true + send_to_confirmation_page: false + use_custom_confirmation_page: false + confirmation_url: null + notify_subscribe_email: derrick@getdrip.com + notify_unsubscribe_email: derrick@getdrip.com + bcc: null + email_count: 10 + active_subscriber_count: 320 + unsubscribed_subscriber_count: 5 + created_at: "2013-06-21T10:31:58Z" + href: "https://api.getdrip.com/v2/9999999/campaigns/123456" + links: + account: "9999999" + forms: ["888"] + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/campaigns" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + response = client.campaigns + + if response.success? + puts response.body["campaigns"] + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const options = { status: "active" }; + + client.listCampaigns(options) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + + /{account_id}/campaigns/{campaign_id}: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + - name: campaign_id + in: path + required: true + description: The ID of the Email Series Campaign. + schema: + type: string + + get: + operationId: fetchCampaign + tags: [Campaigns] + summary: Fetch an Email Series Campaign + x-slate: + intro: "To fetch a specific Email Series Campaign:" + response: + intro: "The response looks like this:" + arguments: none + responses: + "200": + description: >- + The requested Email Series Campaign. The `campaigns` property is an + array of campaign objects. The `forms` property is an array of forms + that feed directly into the campaign. + content: + application/json: + schema: + type: object + properties: + links: + type: object + description: Top-level link data included with all responses containing Email Series Campaign data. + campaigns: + type: array + items: + $ref: "#/components/schemas/Campaign" + linked: + type: object + properties: + forms: + type: array + description: >- + The associated forms created for the Email Series + Campaign. This is only populated if a form is created + for the Email Series Campaign. Refer to Forms for an + overview of the properties returned here. + items: + type: object + example: + links: + campaigns.account: "https://api.getdrip.com/v2/accounts/{campaigns.account}" + campaigns.form: "https://api.getdrip.com/v2/{campaigns.account}/forms/{campaigns.forms}" + campaigns.subscribers: "https://api.getdrip.com/v2/{campaigns.account}/campaigns/{campaigns.id}/subscribers" + campaigns: + - id: "123456" + status: active + name: SEO Email Course + from_name: John Doe + from_email: john@example.com + postal_address: "123 Anywhere St\nFresno, CA 99999" + minutes_from_midnight: 440 + localize_sending_time: true + days_of_the_week_mask: "0111110" + start_immediately: true + double_optin: true + send_to_confirmation_page: false + use_custom_confirmation_page: false + confirmation_url: null + notify_subscribe_email: derrick@getdrip.com + notify_unsubscribe_email: derrick@getdrip.com + bcc: null + email_count: 10 + active_subscriber_count: 320 + unsubscribed_subscriber_count: 5 + created_at: "2013-06-21T10:31:58Z" + href: "https://api.getdrip.com/v2/9999999/campaigns/123456" + links: + account: "9999999" + forms: ["888"] + linked: + forms: [] + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/campaigns/CAMPAIGN_ID" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + campaign_id = 9999999 + response = client.campaign(campaign_id) + + if response.success? + puts response.body + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const campaignId = 9998888; + + client.fetchCampaign(campaignId) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + + /{account_id}/campaigns/{campaign_id}/activate: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + - name: campaign_id + in: path + required: true + description: The ID of the Email Series Campaign. + schema: + type: string + + post: + operationId: activateCampaign + tags: [Campaigns] + summary: Activate an Email Series Campaign + x-slate: + intro: "To activate an Email Series Campaign:" + response: + intro: "Responds with a `204 No Content` if successful. If the Email Series Campaign cannot be activated, returns a `422 Unprocessable Entity`." + arguments: none + responses: + "204": + description: No Content. The Email Series Campaign was activated. + "422": + $ref: "../components.yaml#/components/responses/ValidationError" + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl -X POST "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/campaigns/CAMPAIGN_ID/activate" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + campaign_id = 9999999 + response = client.activate_campaign(campaign_id) + + if response.success? + puts response.body + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const campaignId = 9998888; + + client.activateCampaign(campaignId) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + + /{account_id}/campaigns/{campaign_id}/pause: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + - name: campaign_id + in: path + required: true + description: The ID of the Email Series Campaign. + schema: + type: string + + post: + operationId: pauseCampaign + tags: [Campaigns] + summary: Pause an Email Series Campaign + x-slate: + intro: "To pause an Email Series Campaign:" + response: + intro: "Responds with a `204 No Content` if successful. If the Email Series Campaign cannot be paused, returns a `422 Unprocessable Entity`." + arguments: none + responses: + "204": + description: No Content. The Email Series Campaign was paused. + "422": + $ref: "../components.yaml#/components/responses/ValidationError" + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl -X POST "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/campaigns/CAMPAIGN_ID/pause" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + campaign_id = 9999999 + response = client.pause_campaign(campaign_id) + + if response.success? + puts response.body + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const campaignId = 9998888; + + client.pauseCampaign(campaignId) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + + /{account_id}/campaigns/{campaign_id}/subscribers: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + - name: campaign_id + in: path + required: true + description: The ID of the Email Series Campaign. + schema: + type: string + + get: + operationId: listCampaignSubscribers + tags: [Campaigns] + summary: List all subscribers subscribed to an Email Series Campaign + x-slate: + intro: "To list subscribers on an Email Series Campaign:" + response: + intro: "The response looks like this:" + arguments: auto + parameters: + - name: status + in: query + description: "Optional. The status to filter by: `active`, `unsubscribed`, or `removed`. Defaults to `active`." + schema: + type: string + enum: [active, unsubscribed, removed] + default: active + - name: page + in: query + description: Optional. The page number. Defaults to `1`. + schema: + type: integer + default: 1 + - name: direction + in: query + description: "Optional. The direction to sort the results for `created_at`: `asc` or `desc`. Defaults to `desc`." + schema: + type: string + enum: [asc, desc] + default: desc + - name: per_page + in: query + description: Optional. The number of records to be returned on each page. Defaults to `100`. Maximum `1000`. + schema: + type: integer + default: 100 + maximum: 1000 + responses: + "200": + description: Subscribers on the Email Series Campaign. The `subscribers` property is an array of subscriber objects. + content: + application/json: + schema: + type: object + properties: + links: + type: object + meta: + type: object + properties: + page: + type: integer + direction: + type: string + count: + type: integer + total_pages: + type: integer + total_count: + type: integer + subscribers: + type: array + description: An array of subscriber objects (documented under the Subscribers resource). + items: + type: object + example: + links: {} + meta: + page: 1 + direction: desc + count: 20 + total_pages: 1 + total_count: 20 + subscribers: [] + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/campaigns/CAMPAIGN_ID/subscribers" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + campaign_id = 9999999 + response = client.campaign_subscribers(campaign_id) + + if response.success? + puts response.body["subscribers] + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const campaignId = 9998888; + + client.listAllSubscribesToCampaign(campaignId) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + + post: + operationId: subscribeToCampaign + tags: [Campaigns] + summary: Subscribe someone to an Email Series Campaign + x-slate: + intro: "To start a subscriber on an Email Series Campaign:" + response: + intro: "The response looks like this:" + arguments: auto + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [subscribers] + properties: + subscribers: + type: array + items: + $ref: "#/components/schemas/CampaignSubscriber" + example: + subscribers: + - email: john@acme.com + utc_offset: 660 + double_optin: true + starting_email_index: 0 + reactivate_if_removed: true + custom_fields: + shirt_size: Medium + responses: + "200": + description: The `subscribers` property is an array of one object. + content: + application/json: + schema: + type: object + properties: + links: + type: object + subscribers: + type: array + description: An array of one subscriber object (documented under the Subscribers resource). + items: + type: object + example: + links: {} + subscribers: + - {} + "422": + $ref: "../components.yaml#/components/responses/ValidationError" + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl -X POST "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/campaigns/CAMPAIGN_ID/subscribers" \ + -H "Content-Type: application/json" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: \ + -d @- << EOF + { + "subscribers": [{ + "email": "john@acme.com", + "utc_offset": 660, + "double_optin": true, + "starting_email_index": 0, + "reactivate_if_removed": true, + "custom_fields": { + "shirt_size": "Medium" + } + }] + } + EOF + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + campaign_id = 9999999 + email = "someone@example.com" + options = { + time_zone: "America/Los_Angeles", + custom_fields: { + name: "Jane Doe" + } + } + + response = client.subscribe(email, campaign_id, options) + + if response.success? + # ... + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const campaignId = 9998888; + const payload = { + subscribers: [{ + email: "someone@example.com", + time_zone: "Asia/Kuala_Lumpur", + custom_fields: { + name: "Jane Doe" + } + }] + } + + client.subscribeToCampaign(campaignId, payload) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + + /{account_id}/subscribers/{subscriber_id}/campaign_subscription: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + - name: subscriber_id + in: path + required: true + description: The ID of the subscriber. + schema: + type: string + + get: + operationId: listCampaignSubscriptions + tags: [Campaigns] + summary: List all of a subscriber's Email Series Campaign subscriptions + x-slate: + intro: "To list Email Series Campaign subscriptions for a subscriber:" + response: + intro: "The response looks like this:" + arguments: none + responses: + "200": + description: >- + The subscriber's Email Series Campaign subscriptions. The + `campaign_subscriptions` property is an array of campaign + subscription objects. + content: + application/json: + schema: + type: object + properties: + links: + type: object + meta: + type: object + properties: + page: + type: integer + count: + type: integer + total_pages: + type: integer + total_count: + type: integer + campaign_subscriptions: + type: array + items: + $ref: "#/components/schemas/CampaignSubscription" + example: + links: + campaign_subscriptions.account: "https://api.getdrip.com/v2/accounts/{campaign_subscriptions.account}" + campaign_subscriptions.subscriber: "https://api.getdrip.com/v2/subscribers/{campaign_subscriptions.subscriber}" + meta: + page: 1 + count: 5 + total_pages: 1 + total_count: 5 + campaign_subscriptions: + - id: "123456" + campaign_id: "999999" + status: active + is_complete: false + lap: 1 + last_sent_email_index: 0 + last_sent_email_at: "2016-03-25T11:00:00Z" + links: + account: "9999999" + subscriber: z1togz2hcjrkpp5treip + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/subscribers/SUBSCRIBER_ID/campaign_subscription" \ + -H "Content-Type: application/json" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + subscriber_id = "bihfwo84teh35dgt99" + + response = client.campaign_subscriptions(subscriber_id) + + if response.success? + puts response.body["campaign_subscriptions"] + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const subscriberId = "iuyfgfweufgr9hvrugegff"; + + client.subscriberCampaignSubscriptions(subscriberId) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + +components: + schemas: + Campaign: + type: object + example: + id: "123456" + status: active + name: SEO Email Course + from_name: John Doe + from_email: john@example.com + postal_address: "123 Anywhere St\nFresno, CA 99999" + minutes_from_midnight: 440 + localize_sending_time: true + days_of_the_week_mask: "0111110" + start_immediately: true + double_optin: true + send_to_confirmation_page: false + use_custom_confirmation_page: false + confirmation_url: null + notify_subscribe_email: derrick@getdrip.com + notify_unsubscribe_email: derrick@getdrip.com + bcc: null + email_count: 10 + active_subscriber_count: 320 + unsubscribed_subscriber_count: 5 + created_at: "2013-06-21T10:31:58Z" + href: "https://api.getdrip.com/v2/9999999/campaigns/123456" + links: + account: "9999999" + forms: ["888"] + properties: + id: + type: string + description: A read-only Drip generated unique id used to identify each Email Series Campaign record. + status: + type: string + description: Returns whether the Email Series Campaign is active, paused or in draft. + name: + type: string + description: The private name given to the Email Series Campaign. + from_name: + type: string + description: A "from name" that appears in your sent emails. This setting overrides the account's default from name. + from_email: + type: string + description: A "from email" that appears in your sent emails. This setting overrides the account's default from email. + postal_address: + type: string + description: As required by the [CAN-SPAM Act](http://1.usa.gov/YgrzFP), this is a postal address used for all sent emails. + minutes_from_midnight: + type: integer + description: The number of minutes after midnight for the time of day set for email sending. + localize_sending_time: + type: boolean + description: The scheduled send_at time if set to be sent in the subscriber's time zone. + days_of_the_week_mask: + type: string + description: A representation of the days of week when emails are set to be sent. For example, `1111100` represents sending enabled only for Mondays to Fridays while `1111111` represents sending enabled for all days of the week. + start_immediately: + type: boolean + description: Returns true if the first email in the Email Series Campaign is set to be delivered immediately after a Email Series Campaign subscription. + double_optin: + type: boolean + description: Returns true if double opt-in is enabled for the Email Series Campaign. + send_to_confirmation_page: + type: boolean + description: Deprecated + deprecated: true + use_custom_confirmation_page: + type: boolean + description: Deprecated + deprecated: true + confirmation_url: + type: ["string", "null"] + description: Deprecated + deprecated: true + notify_subscribe_email: + type: string + description: An email address set that receives a notification whenever a subscriber subscribes via a form submission. + notify_unsubscribe_email: + type: string + description: An email address set that receives a notification whenever a subscriber unsubscribes via their subscription management page. + bcc: + type: ["string", "null"] + description: A blind copy email address set for all Email Series Campaign email deliveries. + email_count: + type: integer + description: Returns a count of all emails associated with the Email Series Campaign. Includes all email statuses. + active_subscriber_count: + type: integer + description: Returns a count of all subscribers who are actively subscribed to the Email Series Campaign. + unsubscribed_subscriber_count: + type: integer + description: Returns a count of all subscribers who unsubscribed from the Email Series Campaign via a delivered email. + created_at: + type: string + format: date-time + description: A timestamp representing when the Email Series Campaign was first created. + href: + type: string + description: The url designated for retrieving the Email Series Campaign record via the REST API. + links: + type: object + description: An object containing the REST API URL for the account, any associated Email Series Campaign forms and subscribers subscribed to the Email Series Campaign. + properties: + account: + type: string + forms: + type: array + items: + type: string + + CampaignSubscription: + type: object + properties: + id: + type: string + campaign_id: + type: string + status: + type: string + is_complete: + type: boolean + lap: + type: integer + last_sent_email_index: + type: integer + last_sent_email_at: + type: string + format: date-time + links: + type: object + properties: + account: + type: string + subscriber: + type: string + + CampaignSubscriber: + type: object + required: [email] + properties: + email: + type: string + format: email + description: Required. The subscriber's email address. + user_id: + type: string + description: Optional. A unique identifier for the user in your database, such as a primary key. + time_zone: + type: string + description: Optional. The subscriber's time zone (in Olson format). Defaults to `Etc/UTC` + double_optin: + type: boolean + description: Optional. If `true`, the double opt-in confirmation email is sent; if `false`, the confirmation email is skipped. Defaults to the value set on the Email Series Campaign. + starting_email_index: + type: integer + description: Optional. The index (zero-based) of the email to send first. Defaults to `0`. + custom_fields: + type: object + description: 'Optional. An Object containing custom field data. E.g. `{ "shirt_size": "Medium" }`.' + tags: + type: array + items: + type: string + description: 'Optional. An Array containing one or more tags. E.g. `["Customer", "SEO"]`.' + reactivate_if_removed: + type: boolean + description: Optional. If `true`, re-subscribe the subscriber to the Email Series Campaign if there is a removed subscriber in Drip with the same email address; otherwise, respond with `422 Unprocessable Entity`. Defaults to `true`. + prospect: + type: boolean + description: "Optional. A Boolean specifiying whether we should attach a lead score to the subscriber (when lead scoring is enabled). Defaults to `true`. **Note:** This flag used to be called `potential_lead`, which we will continue to accept for backwards compatibility." + base_lead_score: + type: integer + description: Optional. An Integer specifying the starting value for lead score calculation for this subscriber. Defaults to `30`. + eu_consent: + type: string + description: Optional. A String specifying whether the subscriber `granted` or `denied` GDPR consent. + eu_consent_message: + type: string + description: Optional. A String containing the message the subscriber granted or denied their consent to. diff --git a/spec/fragments/conversions.yaml b/spec/fragments/conversions.yaml new file mode 100644 index 00000000000..5de7eee2257 --- /dev/null +++ b/spec/fragments/conversions.yaml @@ -0,0 +1,358 @@ +tags: + - name: Conversions + description: Conversion goals tracked in an account. See the Events API for recording conversion events. + x-slate: + lead: | + > Conversions are represented as follows: + + ```json + { + "id": "99999", + "status": "active", + "name": "Trial Signup", + "url": "/receipt", + "default_value": 2000, + "counting_method": "one_per_visitor", + "created_at": "2013-06-21T10:31:58Z", + "href": "https://api.getdrip.com/v2/9999999/goals/99999", + "links": { + "account": "9999999" + } + } + ``` + + > All responses containing conversion data also include the following top-level link data: + + ```json + { + "links": { + "goals.account": "https://api.getdrip.com/v2/accounts/{goals.account}" + } + } + ``` + + **Properties** + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyDescription
idA read-only Drip generated unique id used to identify each conversion record.
statusReturns whether the conversion is enabled or disabled.
nameThe private name given to the conversion.
urlA URL used for detecting and recording conversions.
default_valueA default value assigned to a tracked conversion. + Conversion values must be less than 2,147,483,647. +
counting_methodSet either as one_per_visitor or all and determines whether a maximum of one conversion is counted per person or all.
created_atA timestamp representing when the conversion was first created.
hrefThe url designated for retrieving the conversion record via the REST API.
linksAn object containing the REST API URL for the account.
+ + See the Events API for recording conversion events. + +paths: + /{account_id}/goals: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + + get: + operationId: listConversions + tags: [Conversions] + summary: List all conversions + x-slate: + intro: "To list all conversions in an account:" + response: + intro: "The response looks like this:" + body: | + # The goals property is an array of conversion goal objects. + { + "links": { ... }, + "goals": [ ... ] + } + arguments: auto + parameters: + - name: status + in: query + required: false + description: "Optional. The status to filter by: `active`, `disabled`, or `all`. Defaults to `all`." + schema: + type: string + enum: [active, disabled, all] + default: all + - name: sort + in: query + required: false + description: "Optional. Sort results by one of these fields: `created_at` or `name`. Defaults to `created_at`." + schema: + type: string + enum: [created_at, name] + default: created_at + - name: direction + in: query + required: false + description: "Optional. Filter sort direction with: `asc` or `desc`. Defaults to `asc`." + schema: + type: string + enum: [asc, desc] + default: asc + responses: + "200": + description: The `goals` property is an array of conversion goal objects. + content: + application/json: + schema: + type: object + properties: + links: + type: object + description: Top-level link data included in all responses containing conversion data. + properties: + goals.account: + type: string + description: URL template for the REST API account resource. + goals: + type: array + description: The list of conversion goal objects. + items: + $ref: "#/components/schemas/Conversion" + example: + links: + goals.account: "https://api.getdrip.com/v2/accounts/{goals.account}" + goals: + - id: "99999" + status: active + name: Trial Signup + url: /receipt + default_value: 2000 + counting_method: one_per_visitor + created_at: "2013-06-21T10:31:58Z" + href: https://api.getdrip.com/v2/9999999/goals/99999 + links: + account: "9999999" + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/goals" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + response = client.conversions + + if response.success? + puts response.body["goals"] + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const options = { status: "active" }; + + client.listConversions(options) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + + /{account_id}/goals/{conversion_id}: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + - name: conversion_id + in: path + required: true + description: The ID of the conversion. + schema: + type: string + + get: + operationId: fetchConversion + tags: [Conversions] + summary: Fetch a conversion + x-slate: + intro: "To fetch a conversion:" + response: + intro: "The response looks like this:" + body: | + # The goals property is an array of one conversion goal object. + { + "links": { ... }, + "goals": [{ ... }] + } + arguments: none + responses: + "200": + description: The `goals` property is an array of one conversion goal object. + content: + application/json: + schema: + type: object + properties: + links: + type: object + description: Top-level link data included in all responses containing conversion data. + properties: + goals.account: + type: string + description: URL template for the REST API account resource. + goals: + type: array + description: An array containing the requested conversion goal object. + items: + $ref: "#/components/schemas/Conversion" + example: + links: + goals.account: "https://api.getdrip.com/v2/accounts/{goals.account}" + goals: + - id: "99999" + status: active + name: Trial Signup + url: /receipt + default_value: 2000 + counting_method: one_per_visitor + created_at: "2013-06-21T10:31:58Z" + href: https://api.getdrip.com/v2/9999999/goals/99999 + links: + account: "9999999" + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/goals/CONVERSION_ID" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + conversion_id = 9999999 + response = client.conversion(conversion_id) + + if response.success? + puts response.body["goals"] + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const conversionId = 8889999; + + client.fetchConversion(conversionId) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + +components: + schemas: + Conversion: + type: object + properties: + id: + type: string + description: A read-only Drip generated unique id used to identify each conversion record. + status: + type: string + description: Returns whether the conversion is enabled or disabled. + name: + type: string + description: The private name given to the conversion. + url: + type: string + description: A URL used for detecting and recording conversions. + default_value: + type: integer + description: >- + A default value assigned to a tracked conversion. Conversion values + must be less than 2,147,483,647. + counting_method: + type: string + enum: [one_per_visitor, all] + description: >- + Set either as `one_per_visitor` or `all` and determines whether a + maximum of one conversion is counted per person or all. + created_at: + type: string + format: date-time + description: A timestamp representing when the conversion was first created. + href: + type: string + description: The url designated for retrieving the conversion record via the REST API. + links: + type: object + description: An object containing the REST API URL for the account. + properties: + account: + type: string + description: The ID of the account. + example: + id: "99999" + status: active + name: Trial Signup + url: /receipt + default_value: 2000 + counting_method: one_per_visitor + created_at: "2013-06-21T10:31:58Z" + href: https://api.getdrip.com/v2/9999999/goals/99999 + links: + account: "9999999" diff --git a/spec/fragments/custom_fields.yaml b/spec/fragments/custom_fields.yaml new file mode 100644 index 00000000000..b044bea4056 --- /dev/null +++ b/spec/fragments/custom_fields.yaml @@ -0,0 +1,94 @@ +tags: + - name: Custom Fields + description: Custom field identifiers used in an account. +paths: + /{account_id}/custom_field_identifiers: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + + get: + operationId: listCustomFields + tags: [Custom Fields] + summary: List all custom field identifiers used in an account + x-slate: + intro: "To list all custom fields:" + response: + intro: "The response looks like this:" + body: | + { + "custom_field_identifiers": [ "first_name", "last_name" ] + } + sections: | + **Properties** + + + + + + + + + + + + + + +
PropertyDescription
custom_field_identifiersReturns a list of all active custom field identifiers used in the target account.
+ arguments: none + responses: + "200": + description: All custom field identifiers used in the account. + content: + application/json: + schema: + type: object + required: [custom_field_identifiers] + properties: + custom_field_identifiers: + type: array + description: Returns a list of all active custom field identifiers used in the target account. + items: + type: string + example: + custom_field_identifiers: + - first_name + - last_name + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/custom_field_identifiers" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + response = client.custom_fields + + if response.success? + puts response.body["custom_field_identifiers"] + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + + client.listAllCustomFields() + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); diff --git a/spec/fragments/events.yaml b/spec/fragments/events.yaml new file mode 100644 index 00000000000..16105cfe956 --- /dev/null +++ b/spec/fragments/events.yaml @@ -0,0 +1,323 @@ +tags: + - name: Events + description: Custom events recorded for subscribers in an account. + +paths: + /{account_id}/events: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + + post: + operationId: recordEvent + tags: [Events] + summary: Record an event + description: | + If you need to create or update a collection of events at once, use the + Batch API instead. + x-slate: + # Published page reuses the subscribers annotation here. Kept for parity. + intro: "To create or update a subscriber:" + response: + intro: "Responds with a `204 No Content` if successful." + show-description: true + # Carried verbatim: the published table has hand-authored wording + # (and a couple of historical typos) the clean schema does not reproduce. + arguments: | + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyDescription
emailOptional. The subscriber's email address. Either email or id must be included.
idOptional. The subscriber's Drip id. Either email or id must be included.
actionRequired. The name of the action taken. E.g. "Logged in"
prospectOptional. A Boolean specifiying whether we should attach a lead score to the subscriber (when lead scoring is enabled). Defaults to true. + Note: This flag used to be called potential_lead, which we will continue to accept for backwards compatibility.
propertiesOptional. An Object containing custom event properties. If this event is a conversion, include the value (in cents) in the in the properties with a value key. + Conversion values must be less than 2,147,483,647. +
occurred_atOptional. The String time at which the event occurred in ISO-8601 format. Defaults to the current time.
+ requestBody: + required: true + content: + application/json: + schema: + type: object + required: [events] + properties: + events: + type: array + items: + $ref: "#/components/schemas/Event" + example: + events: + - email: john@acme.com + action: Logged in + properties: + affiliate_code: XYZ + occurred_at: "2014-03-22T03:00:00Z" + responses: + "204": + description: No Content. The event was recorded. + "422": + $ref: "../components.yaml#/components/responses/ValidationError" + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl -X POST "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/events" \ + -H "Content-Type: application/json" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: \ + -d @- << EOF + { + "events": [{ + "email": "john@acme.com", + "action": "Logged in", + "properties": { + "affiliate_code": "XYZ" + }, + "occurred_at": "2014-03-22T03:00:00Z" + }] + } + EOF + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + email = "someone@example.com" + action = "Docked with space station" + properties = { + station: "Mars Endeavor" + } + + response = client.track_event(email, action, properties) + + if response.success? + # ... + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const payload = { + email: "john@acme.com", + action: "Logged in", + properties: { + affiliate_code: "XYZ" + } + }; + + client.recordEvent(payload) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + + /{account_id}/event_actions: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + + get: + operationId: listEventActions + tags: [Events] + summary: List all custom events actions used in an account + x-slate: + intro: "To list custom event actions:" + response: + intro: "The response looks like this:" + # Carried verbatim: the published table has a blank line between rows. + arguments: | + + + + + + + + + + + + + + + + + + +
KeyDescription
pageOptional. The page number. Defaults to 1.
per_pageOptional. The number of records to be returned on each page. Defaults to 100. Maximum 1000.
+ parameters: + - name: page + in: query + required: false + description: The page number. Defaults to `1`. + schema: + type: integer + default: 1 + - name: per_page + in: query + required: false + description: >- + The number of records to be returned on each page. Defaults to + `100`. Maximum `1000`. + schema: + type: integer + default: 100 + maximum: 1000 + responses: + "200": + description: The custom event actions used in the account. + content: + application/json: + schema: + type: object + required: [meta, event_actions] + properties: + meta: + type: object + description: Pagination metadata. + properties: + count: + type: integer + description: The number of records on the current page. + page: + type: integer + description: The current page number. + total_count: + type: integer + description: The total number of records. + total_pages: + type: integer + description: The total number of pages. + event_actions: + type: array + description: The list of custom event actions used in the account. + items: + type: string + example: + meta: + count: 2 + page: 1 + total_count: 2 + total_pages: 1 + event_actions: + - Ate a sandwich + - Started a trial + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/event_actions" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + response = client.event_actions + + if response.success? + puts response["event_actions"] + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const options = { per_page: 200 }; + + client.listEventActions(options) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + +components: + schemas: + Event: + type: object + required: [action] + properties: + email: + type: string + format: email + description: >- + Optional. The subscriber's email address. Either `email` or `id` + must be included. + id: + type: string + description: >- + Optional. The subscriber's Drip `id`. Either `email` or `id` must + be included. + action: + type: string + description: Required. The name of the action taken. E.g. "Logged in" + prospect: + type: boolean + default: true + description: >- + Optional. A Boolean specifiying whether we should attach a lead + score to the subscriber (when lead scoring is enabled). Defaults to + `true`. **Note:** This flag used to be called `potential_lead`, + which we will continue to accept for backwards compatibility. + properties: + type: object + description: >- + Optional. An Object containing custom event properties. If this + event is a conversion, include the value (in cents) in the + properties with a `value` key. Conversion values must be less than + 2,147,483,647. + occurred_at: + type: string + format: date-time + description: >- + Optional. The String time at which the event occurred in + [ISO-8601](http://en.wikipedia.org/wiki/ISO_8601) format. Defaults + to the current time. diff --git a/spec/fragments/forms.yaml b/spec/fragments/forms.yaml new file mode 100644 index 00000000000..d414704d8fc --- /dev/null +++ b/spec/fragments/forms.yaml @@ -0,0 +1,485 @@ +tags: + - name: Forms + description: Form widgets and embedded forms in an account. + x-slate: + lead: | + > Forms are represented as follows: + + ```json + { + "id": "77777", + "href": "https://api.getdrip.com/v2/9999999/forms/77777", + "headline": "Long Tail SEO Course", + "description": "Get our FREE email course", + "button_text": "Sign Up!", + "confirmation_heading": "Thank you for subscribing", + "confirmation_text": "Please click the link in your email", + "send_ga_event": true, + "seconds_before_popup": 5, + "days_between_popup": 5, + "days_between_popup_after_close": 10, + "orientation": "bottom_right_tab", + "opacity": 0.8, + "show_labels": false, + "primary_color": "#333333", + "secondary_color": "#d8ab93", + "is_widget_enabled": true, + "whitelist": [ "/landing", "/public" ], + "blacklist": [], + "is_whitelist_enabled": true, + "is_blacklist_enabled": false, + "hide_on_mobile": false, + "is_embeddable": true, + "created_at": "2013-06-21T10:31:58Z", + "links": { + "account": "9999999" + } + } + ``` + + > All responses containing form data also include the following top-level link data: + + ```json + { + "links": { + "forms.account": "https://api.getdrip.com/v2/accounts/{forms.account}" + } + } + ``` + + **Properties** + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyDescription
idA read-only Drip generated unique id used to identify each form record.
hrefThe url designated for retrieving the form record via the REST API.
headlineThe visible headline on the form widget or embedded form.
descriptionA description shown in the body of the form widget or in the embedded form.
button_textThe text shown on the submission button for the form.
confirmation_headingA heading shown after a subscriber submits the form.
confirmation_textThe text shown after a subscriber submits the form.
send_ga_eventReturns true if an event is sent to Google Analytics upon submission.
seconds_before_popupA delay in seconds before the form widget is displayed.
days_between_popupA delay in days between each appearance of the form widget.
days_between_popup_after_closeA delay in days between each appearance of the form widget after a visitor closes the form.
orientationThe position on the page where the form widget is displayed. Possible values are bottom_left_tab, bottom_right_tab, side_left_tab, side_right_tab, embedded or lightbox.
opacityThe opacity set for form field labels.
show_labelsReturns true if form field labels are shown above text boxes.
primary_colorThe tab color for the form.
secondary_colorThe headline color for the form.
is_widget_enabledReturns true if the form widget is enabled and shown.
whitelistAn array of URL paths where the form is shown.
blacklistAn array of URL paths where the form is hidden.
is_whitelist_enabledReturns true if whitelisted URLs are set.
is_blacklist_enabledReturns true if blacklisted URLs are set.
hide_on_mobileReturns true if the form widget is disabled for mobile devices.
is_embeddableDeprecated
created_atA timestamp representing when the form was first created.
linksAn object containing the REST API URL for the account.
+paths: + /{account_id}/forms: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + + get: + operationId: listForms + tags: [Forms] + summary: List all forms + x-slate: + intro: "To list all forms in an account:" + response: + intro: "The response looks like this:" + body: | + # The forms property is an array of form objects. + { + "links": { ... }, + "forms": [ ... ] + } + arguments: none + responses: + "200": + description: The `forms` property is an array of form objects. + content: + application/json: + schema: + type: object + properties: + links: + type: object + description: Top-level link data included in all responses containing form data. + properties: + forms.account: + type: string + description: URL template for the account linked from each form. + forms: + type: array + items: + $ref: "#/components/schemas/Form" + example: + links: + forms.account: https://api.getdrip.com/v2/accounts/{forms.account} + forms: + - id: "77777" + href: https://api.getdrip.com/v2/9999999/forms/77777 + headline: Long Tail SEO Course + description: Get our FREE email course + button_text: Sign Up! + confirmation_heading: Thank you for subscribing + confirmation_text: Please click the link in your email + send_ga_event: true + seconds_before_popup: 5 + days_between_popup: 5 + days_between_popup_after_close: 10 + orientation: bottom_right_tab + opacity: 0.8 + show_labels: false + primary_color: "#333333" + secondary_color: "#d8ab93" + is_widget_enabled: true + whitelist: + - /landing + - /public + blacklist: [] + is_whitelist_enabled: true + is_blacklist_enabled: false + hide_on_mobile: false + is_embeddable: true + created_at: "2013-06-21T10:31:58Z" + links: + account: "9999999" + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/forms" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + response = client.forms + + if response.success? + puts response.body["forms"] + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + + client.listForms() + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + + /{account_id}/forms/{form_id}: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + - name: form_id + in: path + required: true + description: The ID of the form. + schema: + type: string + + get: + operationId: fetchForm + tags: [Forms] + summary: Fetch a form + x-slate: + intro: "To fetch a specific form:" + response: + intro: "The response looks like this:" + body: | + # The forms property is an array of one form object. + { + "links": { ... }, + "forms": [{ ... }] + } + arguments: none + responses: + "200": + description: The `forms` property is an array of one form object. + content: + application/json: + schema: + type: object + properties: + links: + type: object + description: Top-level link data included in all responses containing form data. + properties: + forms.account: + type: string + description: URL template for the account linked from each form. + forms: + type: array + items: + $ref: "#/components/schemas/Form" + example: + links: + forms.account: https://api.getdrip.com/v2/accounts/{forms.account} + forms: + - id: "77777" + href: https://api.getdrip.com/v2/9999999/forms/77777 + headline: Long Tail SEO Course + description: Get our FREE email course + button_text: Sign Up! + confirmation_heading: Thank you for subscribing + confirmation_text: Please click the link in your email + send_ga_event: true + seconds_before_popup: 5 + days_between_popup: 5 + days_between_popup_after_close: 10 + orientation: bottom_right_tab + opacity: 0.8 + show_labels: false + primary_color: "#333333" + secondary_color: "#d8ab93" + is_widget_enabled: true + whitelist: + - /landing + - /public + blacklist: [] + is_whitelist_enabled: true + is_blacklist_enabled: false + hide_on_mobile: false + is_embeddable: true + created_at: "2013-06-21T10:31:58Z" + links: + account: "9999999" + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/forms/FORM_ID" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + form_id = 9999999 + response = client.form(form_id) + + if response.success? + puts response.body["forms"] + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const formId = 9998888; + + client.fetchForm(formId) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + +components: + schemas: + Form: + type: object + properties: + id: + type: string + description: A read-only Drip generated unique id used to identify each form record. + href: + type: string + description: The url designated for retrieving the form record via the REST API. + headline: + type: string + description: The visible headline on the form widget or embedded form. + description: + type: string + description: A description shown in the body of the form widget or in the embedded form. + button_text: + type: string + description: The text shown on the submission button for the form. + confirmation_heading: + type: string + description: A heading shown after a subscriber submits the form. + confirmation_text: + type: string + description: The text shown after a subscriber submits the form. + send_ga_event: + type: boolean + description: Returns true if an event is sent to Google Analytics upon submission. + seconds_before_popup: + type: integer + description: A delay in seconds before the form widget is displayed. + days_between_popup: + type: integer + description: A delay in days between each appearance of the form widget. + days_between_popup_after_close: + type: integer + description: A delay in days between each appearance of the form widget after a visitor closes the form. + orientation: + type: string + description: The position on the page where the form widget is displayed. + enum: + - bottom_left_tab + - bottom_right_tab + - side_left_tab + - side_right_tab + - embedded + - lightbox + opacity: + type: number + description: The opacity set for form field labels. + show_labels: + type: boolean + description: Returns true if form field labels are shown above text boxes. + primary_color: + type: string + description: The tab color for the form. + secondary_color: + type: string + description: The headline color for the form. + is_widget_enabled: + type: boolean + description: Returns true if the form widget is enabled and shown. + whitelist: + type: array + description: An array of URL paths where the form is shown. + items: + type: string + blacklist: + type: array + description: An array of URL paths where the form is hidden. + items: + type: string + is_whitelist_enabled: + type: boolean + description: Returns true if whitelisted URLs are set. + is_blacklist_enabled: + type: boolean + description: Returns true if blacklisted URLs are set. + hide_on_mobile: + type: boolean + description: Returns true if the form widget is disabled for mobile devices. + is_embeddable: + type: boolean + description: Deprecated + deprecated: true + created_at: + type: string + format: date-time + description: A timestamp representing when the form was first created. + links: + type: object + description: An object containing the REST API URL for the account. + properties: + account: + type: string + description: The ID of the account the form belongs to. diff --git a/spec/fragments/orders.yaml b/spec/fragments/orders.yaml new file mode 100644 index 00000000000..a786ff19c1b --- /dev/null +++ b/spec/fragments/orders.yaml @@ -0,0 +1,1314 @@ +tags: + - name: Orders + description: Legacy endpoint for recording orders. Superseded by the Order Activity endpoint, but it will continue to function, with fair notice before retirement. + x-slate: + title: Orders (Legacy) + lead: | + + + > Orders are represented as follows: + + ```json + { + "id": "9999999", + "provider": "shopify", + "upstream_id": "abcdef", + "identifier": "Order_123456", + "amount": 4900, + "tax": 100, + "fees": 0, + "discount": 0, + "permalink": "http://myorders.com/orders/123456", + "currency_code": "USD", + "properties": { + "shirt_size": "Medium", + "color": "red" + }, + "occurred_at": "2013-06-21T10:31:58Z", + "closed_at": "2013-06-21T10:35:58Z", + "cancelled_at": null, + "financial_state": "paid", + "fulfillment_state": "fulfilled", + "billing_address": { + "name": "Bill Billington", + "first_name": "Bill", + "last_name": "Billington", + "company": "Bills R US", + "address_1": "123 Bill St.", + "address_2": "Apt. B", + "city": "Billtown", + "state": "CA", + "zip": "01234", + "country": "United States", + "phone": "555-555-5555", + "email": "bill@bills.com" + }, + "shipping_address": { + "name": "Ship Shippington", + "first_name": "Ship", + "last_name": "Shipington", + "company": "Shipping 4 Less", + "address_1": "123 Ship St.", + "address_2": "null", + "city": "Shipville", + "state": "CA", + "zip": "01234", + "country": "United States", + "phone": "555-555-5555", + "email": "ship@shipping.com" + }, + "items": [{ + "id": "8888888", + "product_id": "765432", + "sku": "4444", + "amount": 4900, + "name": "Canoe", + "quantity": 1, + "upstream_id": "hijkl", + "upstream_product_id": "opqrs", + "upstream_product_variant_id": "zyxwv", + "price": 4900, + "tax": 100, + "fees": 0, + "discount": 100, + "taxable": true, + "properties": { + "color": "black" + } + }], + "links": { + "account": "9999999", + "subscriber": "5555555" + } + } + ``` + + **Properties** + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyDescription
idA read-only Drip generated unique id used to identify each order record.
providerThe identifier for the provider from which the order data was received in lower snake cased form. For example, shopify or my_store
amountThe total amount of the order in cents (e.g. if the order was $49.99, set the amount to 4999).
taxThe tax on the order in cents (e.g. if the order was $1.00, set the tax to 100).
feesThe fees on the order in cents (e.g. if the order was $1.00, set the fees to 100).
discountThe discount on the order in cents (e.g. if the order was $5.00, set the discount to 500).
permalinkA URL for the human-readable interface to view the order details.
currency_codeThe alphabetic ISO 4217 code for the currency of the purchase.
upstream_idA unique, internal id for the order (generally the primary key generated by the order management system).
identifierA unique, external identifier for the order. This will be displayed in the Drip UI and should correspond to what a customer sees on + invoices, email confirmations, etc.
propertiesAn Object containing properties about the order. E.g. { "size": "large" }
occurred_atThe String time at which the order occurred in ISO-8601 format. Defaults to the current time.
closed_atThe String time at which the order closed in ISO-8601 format.
cancelled_atThe String time at which the order was cancelled in ISO-8601 format. For an order that was not cancelled, this will be nil.
financial_stateThe financial status of the order. One of the following values: pending, authorized, partially_paid, paid, partially_refunded, refunded, voided.
fulfillment_stateThe fulfillment status of the order. One of the following values: not_fulfilled, partially_fulfilled, fulfilled.
billing_address + An object containing billing address information. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyDescription
nameThe full name on the billing address
first_nameThe first name on the billing address
last_nameThe last name on the billing address
companyThe company on the billing address
address_1The billing street address
address_2Additional line of the billing street address
cityThe billing address city
stateThe billing address state
zipThe billing address zip code
countryThe billing address country
phoneThe phone number associated with the billing address
emailThe email associated with the billing address
+
shipping_address + An object containing shipping address information. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyDescription
nameThe full name on the shipping address
first_nameThe first name on the shipping address
last_nameThe last name on the shipping address
companyThe company on the shipping address
address_1The shipping street address
address_2Additional line of the shipping street address
cityThe shipping address city
stateThe shipping address state
zipThe shipping address zip code
countryThe shipping address country
phoneThe phone number associated with the shipping address
emailThe email associated with the shipping address
+
items + An Array of objects containing information about specific order items. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyDescription
nameThe product name
amountThe line total (in cents).
priceThe item price (in cents).
taxThe item tax (in cents).
feesThe item fees (in cents).
discountThe item discount (in cents).
quantityThe quantity of the item ordered (if omitted, defaults to 1).
product_idA unique identifier for the specific product.
upstream_idA unique identifier for the item from the provider.
upstream_product_idA unique identifier for the specific product from the provider.
upstream_product_variant_idA unique identifier for the specific product variant from the provider.
skuThe product SKU number.
taxableBoolean indicating whether the item is taxable.
propertiesAn Object containing properties about the line item.
+
linksAn object containing the REST API URL for the account.
+ footer: | + ## Order events + + Drip automatically records a number of events in the life cycle of an order. You can find + triggers for these events by selecting Drip Order API from the provider dropdown in your Drip automation. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
EventDescription
Placed an orderThis event is fired when an order is created.
Updated an orderThis event is fired when an existing order is updated.
Paid an orderThis event is fired when the financial_state on an order is set to paid.
Fulfilled an orderThis event is fired when the fulfillment_state on an order is set to fulfilled.
Canceled an orderThis event is fired when the cancelled_at time on an order is set.
Refunded an orderThis event is fired when a refund is created for an order.
+ +paths: + /{account_id}/orders: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + + post: + operationId: createOrUpdateOrder + tags: [Orders] + summary: Create or update an order + description: | + When an order is created, the subscriber's `lifetime_value` attribute + will be automatically incremented by the total amount of the order. + + To update an existing order, include the `provider` and `upstream_id` + for that order in the payload. + + **Note**: A matching subscriber record must already be present in Drip + before an order can be created. + x-slate: + intro: "To create or update an order for a subscriber:" + code-order: [ruby, shell] + response: + intro: "Responds with a `202 Accepted` and an empty JSON response:" + # Published page hand-formats this description; carried verbatim. + sections: | + When an order is created, the subscriber's `lifetime_value` attribute + will be automatically incremented by the total amount of the order. + + To update an existing order, include the provider and upstream_id for that order in the payload. + + **Note**: A matching subscriber record must already be present in Drip before an order can be created. + arguments: | + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyDescription
emailOptional. The email address of the order's subscriber. Either email or id must be included.
idOptional. The Drip id of the order's subscriber. Either email or id must be included.
amountRequired. The total amount of the order in cents (e.g. if the order was $49.99, set the amount to 4999).
providerOptional. Required for updates. The identifier for the provider from which the order data was received in lower snake cased form. For example, shopify or my_store. Used to identify the order for updates.
taxOptional. The tax on the order in cents (e.g. if the order was $1.00, set the tax to 100).
feesOptional. The fees on the order in cents (e.g. if the order was $1.00, set the fees to 100).
discountOptional. The discount on the order in cents (e.g. if the order was $5.00, set the discount to 500).
permalinkOptional. A URL for the human-readable interface to view the order details.
currency_codeOptional. The alphabetic ISO 4217 code for the currency of the purchase.
upstream_idOptional. Required for updates. A unique, internal id for the order (generally the primary key generated by the order management system). + Used to identify the order for updates.
identifierOptional. A unique, external identifier for the order. This will be displayed in the Drip UI and should correspond to what a customer sees on + invoices, email confirmations, etc.
propertiesOptional. An Object containing properties about the order. E.g. { "size": "large" }
occurred_atOptional. The String time at which the order occurred in ISO-8601 format. Defaults to the current time.
closed_atOptional. The String time at which the order closed in ISO-8601 format.
cancelled_atOptional. The String time at which the order was cancelled in ISO-8601 format. For an order that was not cancelled, this will be nil.
financial_stateOptional. The financial status of the order. One of the following values: pending, authorized, partially_paid, paid, partially_refunded, refunded, voided.
fulfillment_stateOptional. The fulfillment status of the order. One of the following values: not_fulfilled, partially_fulfilled, fulfilled.
billing_address + Optional. An object containing billing address information. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyDescription
nameThe full name on the billing address
first_nameThe first name on the billing address
last_nameThe last name on the billing address
companyThe company on the billing address
address_1The billing street address
address_2Additional line of the billing street address
cityThe billing address city
stateThe billing address state
zipThe billing address zip code
countryThe billing address country
phoneThe phone number associated with the billing address
emailThe email associated with the billing address
+
shipping_address + Optional. An object containing shipping address information. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyDescription
nameThe full name on the shipping address
first_nameThe first name on the shipping address
last_nameThe last name on the shipping address
companyThe company on the shipping address
address_1The shipping street address
address_2Additional line of the shipping street address
cityThe shipping address city
stateThe shipping address state
zipThe shipping address zip code
countryThe shipping address country
phoneThe phone number associated with the shipping address
emailThe email associated with the shipping address
+
items + Optional. An Array of objects containing information about specific order items. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyDescription
nameRequired. The product name
amountRequired. The line total (in cents).
priceThe item price (in cents).
taxThe item tax (in cents).
feesThe item fees (in cents).
discountThe item discount (in cents).
quantityThe quantity of the item ordered (if omitted, defaults to 1).
product_idA unique identifier for the specific product.
upstream_idA unique identifier for the item from the provider.
upstream_product_idA unique identifier for the specific product from the provider.
upstream_product_variant_idA unique identifier for the specific product variant from the provider.
skuThe product SKU number.
taxableBoolean indicating whether the item is taxable.
propertiesAn Object containing properties about the line item.
+
+ requestBody: + required: true + content: + application/json: + schema: + type: object + required: [orders] + properties: + orders: + type: array + description: The list of orders to create or update. + items: + $ref: "#/components/schemas/Order" + example: + orders: + - email: "john@acme.com" + provider: "shopify" + upstream_id: "abcdef" + identifier: "Order_123456" + amount: 4900 + tax: 100 + fees: 0 + discount: 0 + permalink: "http://myorders.com/orders/123456" + currency_code: "USD" + properties: + shirt_size: "Medium" + color: "red" + occurred_at: "2013-06-21T10:31:58Z" + closed_at: "2013-06-21T10:35:58Z" + financial_state: "paid" + fulfillment_state: "fulfilled" + billing_address: + name: "Bill Billington" + first_name: "Bill" + last_name: "Billington" + company: "Bills R US" + address_1: "123 Bill St." + address_2: "Apt. B" + city: "Billtown" + state: "CA" + zip: "01234" + country: "United States" + phone: "555-555-5555" + email: "bill@bills.com" + shipping_address: + name: "Ship Shippington" + first_name: "Ship" + last_name: "Shipington" + company: "Shipping 4 Less" + address_1: "123 Ship St." + address_2: "null" + city: "Shipville" + state: "CA" + zip: "01234" + country: "United States" + phone: "555-555-5555" + email: "ship@shipping.com" + items: + - id: "8888888" + product_id: "765432" + sku: "4444" + amount: 4900 + name: "Canoe" + quantity: 1 + upstream_id: "hijkl" + upstream_product_id: "opqrs" + upstream_product_variant_id: "zyxwv" + price: 4900 + tax: 100 + fees: 0 + discount: 100 + taxable: true + properties: + color: "black" + responses: + "202": + description: Accepted. Responds with an empty JSON response. + content: + application/json: + schema: + type: object + example: {} + "422": + $ref: "../components.yaml#/components/responses/ValidationError" + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl -X POST "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/orders" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -H "Content-Type: application/json" \ + -u YOUR_API_KEY: \ + -d @- << EOF + { + "orders": [{ + "email": "john@acme.com", + "provider": "shopify", + "upstream_id": "abcdef", + "identifier": "Order_123456", + "amount": 4900, + "tax": 100, + "fees": 0, + "discount": 0, + "permalink": "http://myorders.com/orders/123456", + "currency_code": "USD", + "properties": { + "shirt_size": "Medium", + "color": "red" + }, + "occurred_at": "2013-06-21T10:31:58Z", + "closed_at": "2013-06-21T10:35:58Z", + "financial_state": "paid", + "fulfillment_state": "fulfilled", + "billing_address": { + "name": "Bill Billington", + "first_name": "Bill", + "last_name": "Billington", + "company": "Bills R US", + "address_1": "123 Bill St.", + "address_2": "Apt. B", + "city": "Billtown", + "state": "CA", + "zip": "01234", + "country": "United States", + "phone": "555-555-5555", + "email": "bill@bills.com" + }, + "shipping_address": { + "name": "Ship Shippington", + "first_name": "Ship", + "last_name": "Shipington", + "company": "Shipping 4 Less", + "address_1": "123 Ship St.", + "address_2": "null", + "city": "Shipville", + "state": "CA", + "zip": "01234", + "country": "United States", + "phone": "555-555-5555", + "email": "ship@shipping.com" + }, + "items": [{ + "id": "8888888", + "product_id": "765432", + "sku": "4444", + "amount": 4900, + "name": "Canoe", + "quantity": 1, + "upstream_id": "hijkl", + "upstream_product_id": "opqrs", + "upstream_product_variant_id": "zyxwv", + "price": 4900, + "tax": 100, + "fees": 0, + "discount": 100, + "taxable": true, + "properties": { + "color": "black" + } + }] + }] + } + EOF + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + order = { + provider: "shopify", + upstream_id: "abcdef", + identifier: "Order_123456", + amount: 4900, + tax: 100, + fees: 0, + discount: 0, + permalink: "http://myorders.com/orders/123456", + currency_code: "USD", + properties: { + shirt_size: "Medium", + color: "red" + }, + occurred_at: "2013-06-21T10:31:58Z", + closed_at: "2013-06-21T10:35:58Z", + financial_state: "paid", + fulfillment_state: "fulfilled", + billing_address: { + name: "Bill Billington", + first_name: "Bill", + last_name: "Billington", + company: "Bills R US", + address_1: "123 Bill St.", + address_2: "Apt. B", + city: "Billtown", + state: "CA", + zip: "01234", + country: "United States", + phone: "555-555-5555", + email: "bill@bills.com" + }, + shipping_address: { + name: "Ship Shippington", + first_name: "Ship", + last_name: "Shipington", + company: "Shipping 4 Less", + address_1: "123 Ship St.", + address_2: "null", + city: "Shipville", + state: "CA", + zip: "01234", + country: "United States", + phone: "555-555-5555", + email: "ship@shipping.com" + }, + items: [{ + id: "8888888", + product_id: "765432", + sku: "4444", + amount: 4900, + name: "Canoe", + quantity: 1, + upstream_id: "hijkl", + upstream_product_id: "opqrs", + upstream_product_variant_id: "zyxwv", + price: 4900, + tax: 100, + fees: 0, + discount: 100, + taxable: true, + properties: { + color: "black" + } + }] + } + + response = client.create_or_update_order("john@acme.com", order) + + if response.success? + # ... + end + + /{account_id}/refunds: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + + post: + operationId: createOrUpdateRefund + tags: [Orders] + summary: Create or update a refund + description: | + When a refund is created, the subscriber's `lifetime_value` attribute + will be automatically decremented by the total amount of the refund. + + To update an existing refund, include the `upstream_id` for that + refund in the payload. + x-slate: + intro: "To create or update a refund for an order:" + response: + intro: "Responds with a `202 Accepted` and an empty JSON response:" + sections: | + When a refund is created, the subscriber's `lifetime_value` attribute + will be automatically decremented by the total amount of the refund. + + To update an existing refund, include the upstream_id for that refund in the payload. + arguments: | + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyDescription
providerRequired. The provider for the Order being refunded.
order_upstream_idRequired. The upstream_id for the Order being refunded.
amountRequired. The amount of the refund.
upstream_idOptional. Required for updates. The unique id of refund in the order management system. Used to identify the refund for updates.
noteOptional. A note about the refund.
processed_atOptional. The String time at which the refund was processed in ISO-8601 format.
+ requestBody: + required: true + content: + application/json: + schema: + type: object + required: [refunds] + properties: + refunds: + type: array + description: The list of refunds to create or update. + items: + $ref: "#/components/schemas/Refund" + example: + refunds: + - provider: "shopify" + order_upstream_id: "abcdef" + upstream_id: "tuvwx" + amount: 2000 + note: "Incorrect size" + processed_at: "2013-06-22T10:41:11Z" + responses: + "202": + description: Accepted. Responds with an empty JSON response. + content: + application/json: + schema: + type: object + example: {} + "422": + $ref: "../components.yaml#/components/responses/ValidationError" + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl -X POST "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/refunds" \ + -H "Content-Type: application/json" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: \ + -d @- << EOF + { + "refunds": [{ + "provider": "shopify", + "order_upstream_id": "abcdef", + "upstream_id": "tuvwx", + "amount": 2000, + "note": "Incorrect size", + "processed_at": "2013-06-22T10:41:11Z" + }] + } + EOF + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + refund = { + provider: "shopify", + order_upstream_id: "abcdef", + upstream_id: "tuvwx", + amount: 2000, + note: "Incorrect size", + processed_at: "2013-06-22T10:41:11Z" + } + + response = client.create_or_update_refund(refund) + + if response.success? + # ... + end + +components: + schemas: + Order: + type: object + description: | + An order to create or update. Either `email` or `id` must be included + to identify the order's subscriber. + required: [amount] + properties: + email: + type: string + format: email + description: The email address of the order's subscriber. Either `email` or `id` must be included. + id: + type: string + description: The Drip `id` of the order's subscriber. Either `email` or `id` must be included. + amount: + type: integer + description: The total amount of the order in cents (e.g. if the order was $49.99, set the amount to `4999`). + provider: + type: string + description: Required for updates. The identifier for the provider from which the order data was received in lower snake cased form. For example, `shopify` or `my_store`. Used to identify the order for updates. + tax: + type: integer + description: The tax on the order in cents (e.g. if the order was $1.00, set the tax to `100`). + fees: + type: integer + description: The fees on the order in cents (e.g. if the order was $1.00, set the fees to `100`). + discount: + type: integer + description: The discount on the order in cents (e.g. if the order was $5.00, set the discount to `500`). + permalink: + type: string + description: A URL for the human-readable interface to view the order details. + currency_code: + type: string + description: The alphabetic [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) code for the currency of the purchase. + upstream_id: + type: string + description: Required for updates. A unique, internal id for the order (generally the primary key generated by the order management system). Used to identify the order for updates. + identifier: + type: string + description: A unique, external identifier for the order. This will be displayed in the Drip UI and should correspond to what a customer sees on invoices, email confirmations, etc. + properties: + type: object + description: 'An Object containing properties about the order. E.g. `{ "size": "large" }`' + occurred_at: + type: string + description: The String time at which the order occurred in [ISO-8601](http://en.wikipedia.org/wiki/ISO_8601) format. Defaults to the current time. + closed_at: + type: string + description: The String time at which the order closed in [ISO-8601](http://en.wikipedia.org/wiki/ISO_8601) format. + cancelled_at: + type: string + description: The String time at which the order was cancelled in [ISO-8601](http://en.wikipedia.org/wiki/ISO_8601) format. For an order that was not cancelled, this will be `nil`. + financial_state: + type: string + description: The financial status of the order. + enum: + - pending + - authorized + - partially_paid + - paid + - partially_refunded + - refunded + - voided + fulfillment_state: + type: string + description: The fulfillment status of the order. + enum: + - not_fulfilled + - partially_fulfilled + - fulfilled + billing_address: + $ref: "#/components/schemas/OrderAddress" + description: An object containing billing address information. + shipping_address: + $ref: "#/components/schemas/OrderAddress" + description: An object containing shipping address information. + items: + type: array + description: An Array of objects containing information about specific order items. + items: + $ref: "#/components/schemas/OrderItem" + + OrderAddress: + type: object + description: A billing or shipping address on an order. + properties: + name: + type: string + description: The full name on the address. + first_name: + type: string + description: The first name on the address. + last_name: + type: string + description: The last name on the address. + company: + type: string + description: The company on the address. + address_1: + type: string + description: The street address. + address_2: + type: string + description: Additional line of the street address. + city: + type: string + description: The address city. + state: + type: string + description: The address state. + zip: + type: string + description: The address zip code. + country: + type: string + description: The address country. + phone: + type: string + description: The phone number associated with the address. + email: + type: string + description: The email associated with the address. + + OrderItem: + type: object + description: A line item on an order. + required: [name, amount] + properties: + name: + type: string + description: The product name. + amount: + type: integer + description: The line total (in cents). + price: + type: integer + description: The item price (in cents). + tax: + type: integer + description: The item tax (in cents). + fees: + type: integer + description: The item fees (in cents). + discount: + type: integer + description: The item discount (in cents). + quantity: + type: integer + description: The quantity of the item ordered (if omitted, defaults to 1). + product_id: + type: string + description: A unique identifier for the specific product. + upstream_id: + type: string + description: A unique identifier for the item from the provider. + upstream_product_id: + type: string + description: A unique identifier for the specific product from the provider. + upstream_product_variant_id: + type: string + description: A unique identifier for the specific product variant from the provider. + sku: + type: string + description: The product SKU number. + taxable: + type: boolean + description: Boolean indicating whether the item is taxable. + properties: + type: object + description: An Object containing properties about the line item. + id: + type: string + description: A unique id for the line item. Appears in the request and representation examples but is not documented in the arguments table. + + Refund: + type: object + description: A refund to create or update for an order. + required: [provider, order_upstream_id, amount] + properties: + provider: + type: string + description: The provider for the Order being refunded. + order_upstream_id: + type: string + description: The `upstream_id` for the Order being refunded. + amount: + type: integer + description: The amount of the refund. + upstream_id: + type: string + description: Required for updates. The unique id of refund in the order management system. Used to identify the refund for updates. + note: + type: string + description: A note about the refund. + processed_at: + type: string + description: The String time at which the refund was processed in [ISO-8601](http://en.wikipedia.org/wiki/ISO_8601) format. diff --git a/spec/fragments/subscribers.yaml b/spec/fragments/subscribers.yaml new file mode 100644 index 00000000000..9773ac977c3 --- /dev/null +++ b/spec/fragments/subscribers.yaml @@ -0,0 +1,1087 @@ +tags: + - name: Subscribers + description: People in an account. All subscriber API endpoints only work with your active people; attempting to modify or delete an inactive person will result in an error. + x-slate: + represent: + - intro: "Subscribers are represented as follows:" + schema: Subscriber + - intro: "All responses containing subscriber data also include the following top-level link data:" + json: | + { + "links": { + "subscribers.account": "https://api.getdrip.com/v2/accounts/{subscribers.account}" + } + } + properties: Subscriber + lead_extra: | + **Note:** All subscriber API endpoints only work with your active people. Attempting to modify or delete an inactive person will result in an error. + +paths: + /{account_id}/subscribers: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + + post: + operationId: createOrUpdateSubscriber + tags: [Subscribers] + summary: Create or update a subscriber + x-slate: + order: 0 + intro: "To create or update a subscriber:" + response: + intro: "The response looks like this:" + show-description: true + arguments: auto + description: | + If you need to create or update a collection of subscribers at once, + use our [batch API](/) instead. + + **Note:** Concurrently updating the same subscriber via multiple API + calls is not supported and will fail with a rate limit error and the + message "Too many concurrent requests for the same subscriber". You + should retry the call after a short wait period to let the other + requests to the same subscriber complete. Triggering this rate limit + does not mean you've consumed your overall API rate limited capacity. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [subscribers] + properties: + subscribers: + type: array + items: + $ref: "#/components/schemas/SubscriberRequest" + example: + subscribers: + - email: john@acme.com + time_zone: America/Los_Angeles + custom_fields: + shirt_size: Medium + responses: + "200": + description: The created or updated subscriber. The `subscribers` property is an array of one object. + content: + application/json: + schema: + type: object + required: [subscribers] + properties: + links: + type: object + description: Top-level link data included with all responses containing subscriber data. + properties: + "subscribers.account": + type: string + subscribers: + type: array + description: An array of one subscriber object. + items: + $ref: "#/components/schemas/Subscriber" + example: + links: + subscribers.account: "https://api.getdrip.com/v2/accounts/{subscribers.account}" + subscribers: + - id: z1togz2hcjrkpp5treip + status: active + email: john@acme.com + first_name: John + last_name: Doe + address1: 123 Main St. + address2: Suite 200 + city: Los Angeles + state: CA + zip: "90210" + country: US + phone: 555-555-5555 + sms_number: "+16125551212" + sms_consent: true + eu_consent: granted + time_zone: America/Los_Angeles + utc_offset: -440 + visitor_uuid: sa8f7sdf78sdsdahf788d7asf8sd + custom_fields: + shirt_size: Medium + tags: [Customer, SEO] + ip_address: 111.111.111.11 + user_agent: Chrome/36.0.1985.143 + original_referrer: "http://www.getdrip.com" + landing_url: "https://www.getdrip.com/docs/rest-api" + prospect: true + lead_score: 72 + lifetime_value: 10000 + created_at: "2013-06-21T10:31:58Z" + href: "https://api.getdrip.com/v2/9999999/subscribers/12345" + user_id: "12345" + base_lead_score: 30 + links: + account: "9999999" + "422": + $ref: "../components.yaml#/components/responses/ValidationError" + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl -X POST "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/subscribers" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -H 'Content-Type: application/json' \ + -u YOUR_API_KEY: \ + -d @- << EOF + { + "subscribers": [{ + "email": "john@acme.com", + "time_zone": "America/Los_Angeles", + "custom_fields": { + "shirt_size": "Medium" + } + }] + } + EOF + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + subscriber_email = "john@acme.com" + options = { + time_zone: "America/Los_Angeles", + custom_fields: { + shirt_size: "Medium" + } + } + + response = client.create_or_update_subscriber(subscriber_email, options) + + if response.success? + puts response.body["subscribers"] + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const payload = { + email: "john@acme.com", + time_zone: "America/Los_Angeles", + custom_fields: { + shirt_size: "Medium" + } + }; + + client.createUpdateSubscriber(payload) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + + get: + operationId: listSubscribers + tags: [Subscribers] + summary: List all subscribers + x-slate: + order: 1 + intro: "To list subscribers:" + response: + intro: "The response looks like this:" + arguments: auto + parameters: + - name: status + in: query + required: false + description: >- + Filter by one of the following statuses: `all`, `active`, + `unsubscribed`, `active_or_unsubscribed` or `undeliverable`. + Defaults to `active`. + schema: + type: string + enum: [all, active, unsubscribed, active_or_unsubscribed, undeliverable] + default: active + - name: tags + in: query + required: false + description: A comma separated list of tags. When included, returns only subscribers who have at least one of the listed tags. + schema: + type: string + - name: subscribed_before + in: query + required: false + description: A [ISO-8601](http://en.wikipedia.org/wiki/ISO_8601) datetime. When included, returns only subscribers who were created before the date. Eg. `"2017-01-01T00:00:00Z"` + schema: + type: string + format: date-time + - name: subscribed_after + in: query + required: false + description: A [ISO-8601](http://en.wikipedia.org/wiki/ISO_8601) datetime. When included, returns only subscribers who were created after the date. Eg. `"2016-01-01T00:00:00Z"` + schema: + type: string + format: date-time + - name: page + in: query + required: false + description: The page number. Defaults to `1`. + schema: + type: integer + default: 1 + - name: per_page + in: query + required: false + description: The number of records to be returned on each page. Defaults to `100`. Maximum `1000`. + schema: + type: integer + default: 100 + maximum: 1000 + responses: + "200": + description: A list of subscribers. The `subscribers` property is an array of subscriber objects; the `meta` property contains pagination information. + content: + application/json: + schema: + type: object + required: [subscribers] + properties: + links: + type: object + description: Top-level link data included with all responses containing subscriber data. + properties: + "subscribers.account": + type: string + meta: + type: object + description: Pagination information. + properties: + page: + type: integer + count: + type: integer + total_pages: + type: integer + total_count: + type: integer + subscribers: + type: array + description: An array of subscriber objects. + items: + $ref: "#/components/schemas/Subscriber" + example: + links: + subscribers.account: "https://api.getdrip.com/v2/accounts/{subscribers.account}" + meta: + page: 1 + count: 5 + total_pages: 1 + total_count: 5 + subscribers: + - id: z1togz2hcjrkpp5treip + status: active + email: john@acme.com + first_name: John + last_name: Doe + address1: 123 Main St. + address2: Suite 200 + city: Los Angeles + state: CA + zip: "90210" + country: US + phone: 555-555-5555 + sms_number: "+16125551212" + sms_consent: true + eu_consent: granted + time_zone: America/Los_Angeles + utc_offset: -440 + visitor_uuid: sa8f7sdf78sdsdahf788d7asf8sd + custom_fields: + shirt_size: Medium + tags: [Customer, SEO] + ip_address: 111.111.111.11 + user_agent: Chrome/36.0.1985.143 + original_referrer: "http://www.getdrip.com" + landing_url: "https://www.getdrip.com/docs/rest-api" + prospect: true + lead_score: 72 + lifetime_value: 10000 + created_at: "2013-06-21T10:31:58Z" + href: "https://api.getdrip.com/v2/9999999/subscribers/12345" + user_id: "12345" + base_lead_score: 30 + links: + account: "9999999" + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/subscribers" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + response = client.subscribers + + if response.success? + puts response.body["subscribers"] + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const options = { status: "active", subscribed_before: "2017-01-01T00:00:00Z" }; + + client.listSubscribers(options) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + + /{account_id}/subscribers/{id_or_email}: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + - name: id_or_email + in: path + required: true + description: The subscriber's ID or email address. The fetch endpoint also accepts a visitor UUID. + schema: + type: string + + get: + operationId: fetchSubscriber + tags: [Subscribers] + summary: Fetch a subscriber + x-slate: + order: 2 + intro: "To fetch a subscriber:" + response: + intro: "The response looks like this:" + arguments: none + responses: + "200": + description: The requested subscriber. The `subscribers` property is an array of one object. + content: + application/json: + schema: + type: object + required: [subscribers] + properties: + links: + type: object + description: Top-level link data included with all responses containing subscriber data. + properties: + "subscribers.account": + type: string + subscribers: + type: array + description: An array of one subscriber object. + items: + $ref: "#/components/schemas/Subscriber" + example: + links: + subscribers.account: "https://api.getdrip.com/v2/accounts/{subscribers.account}" + subscribers: + - id: z1togz2hcjrkpp5treip + status: active + email: john@acme.com + first_name: John + last_name: Doe + address1: 123 Main St. + address2: Suite 200 + city: Los Angeles + state: CA + zip: "90210" + country: US + phone: 555-555-5555 + sms_number: "+16125551212" + sms_consent: true + eu_consent: granted + time_zone: America/Los_Angeles + utc_offset: -440 + visitor_uuid: sa8f7sdf78sdsdahf788d7asf8sd + custom_fields: + shirt_size: Medium + tags: [Customer, SEO] + ip_address: 111.111.111.11 + user_agent: Chrome/36.0.1985.143 + original_referrer: "http://www.getdrip.com" + landing_url: "https://www.getdrip.com/docs/rest-api" + prospect: true + lead_score: 72 + lifetime_value: 10000 + created_at: "2013-06-21T10:31:58Z" + href: "https://api.getdrip.com/v2/9999999/subscribers/12345" + user_id: "12345" + base_lead_score: 30 + links: + account: "9999999" + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/subscribers/ID_OR_EMAIL_OR_VISITOR_UUID" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + subscriber_email = "john@acme.com" + + response = client.subscriber(subscriber_email) + + if response.success? + puts response.body["subscribers"] + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const idOrEmail = "someone@example.com"; + + client.fetchSubscriber(idOrEmail) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + + delete: + operationId: deleteSubscriber + tags: [Subscribers] + summary: Delete a subscriber + x-slate: + order: 5 + intro: "To delete a subscriber:" + response: + intro: "Responds with `204 No Content` if successful." + arguments: none + responses: + "204": + description: No Content. The subscriber was deleted. + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl -X DELETE "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/subscribers/ID_OR_EMAIL" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + subscriber_email = "john@acme.com" + response = client.delete_subscriber(subscriber_email) + + if response.success? + # ... + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const idOrEmail = "someone@example.com"; + + client.deleteSubscriber(idOrEmail) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + + /{account_id}/subscribers/{id_or_email}/remove: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + - name: id_or_email + in: path + required: true + description: The subscriber's ID or email address. + schema: + type: string + + post: + operationId: removeSubscriberFromCampaigns + tags: [Subscribers] + summary: Remove a subscriber from one or all Email Series Campaigns + description: | + This endpoint was previously labeled `unsubscribe`. + x-slate: + order: 3 + intro: "To remove a subscriber from all Email Series Campaigns:" + response: + intro: "The response looks like this:" + show-description: true + arguments: auto + parameters: + - name: campaign_id + in: query + required: false + description: The Email Series Campaign from which to remove the subscriber. Defaults to all. + schema: + type: integer + responses: + "200": + description: The removed subscriber. The `subscribers` property is an array of one object. + content: + application/json: + schema: + type: object + required: [subscribers] + properties: + links: + type: object + description: Top-level link data included with all responses containing subscriber data. + properties: + "subscribers.account": + type: string + subscribers: + type: array + description: An array of one subscriber object. + items: + $ref: "#/components/schemas/Subscriber" + example: + links: + subscribers.account: "https://api.getdrip.com/v2/accounts/{subscribers.account}" + subscribers: + - id: z1togz2hcjrkpp5treip + status: active + email: john@acme.com + first_name: John + last_name: Doe + address1: 123 Main St. + address2: Suite 200 + city: Los Angeles + state: CA + zip: "90210" + country: US + phone: 555-555-5555 + sms_number: "+16125551212" + sms_consent: true + eu_consent: granted + time_zone: America/Los_Angeles + utc_offset: -440 + visitor_uuid: sa8f7sdf78sdsdahf788d7asf8sd + custom_fields: + shirt_size: Medium + tags: [Customer, SEO] + ip_address: 111.111.111.11 + user_agent: Chrome/36.0.1985.143 + original_referrer: "http://www.getdrip.com" + landing_url: "https://www.getdrip.com/docs/rest-api" + prospect: true + lead_score: 72 + lifetime_value: 10000 + created_at: "2013-06-21T10:31:58Z" + href: "https://api.getdrip.com/v2/9999999/subscribers/12345" + user_id: "12345" + base_lead_score: 30 + links: + account: "9999999" + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + # To remove a subscriber from all Email Series Campaigns: + + curl -X POST "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/subscribers/ID_OR_EMAIL/remove" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + + # To remove a subscriber from a specific campaign: + + curl -X POST "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/subscribers/ID_OR_EMAIL/remove?campaign_id=CAMPAIGN_ID" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: ruby + label: Ruby + source: | + ## + # To remove a subscriber from all Email Series Campaigns: + ## + + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + subscriber_email = "john@acme.com" + response = client.unsubscribe(subscriber_email) + + if response.success? + puts response.body["subscribers"] + end + + ## + # To remove a subscriber from a specific campaign: + ## + + subscriber_email = "john@acme.com" + options = { + campaign_id: 9999999 + } + + response = client.unsubscribe(subscriber_email, options) + + if response.success? + puts response.body["subscribers"] + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const idOrEmail = "someone@example.com"; + const campaignId = 9998888; + + client.unsubscribeFromCampaign(idOrEmail, campaignId) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + + /{account_id}/subscribers/{id_or_email}/unsubscribe_all: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + - name: id_or_email + in: path + required: true + description: The subscriber's ID or email address. + schema: + type: string + + post: + operationId: unsubscribeFromAllMailings + tags: [Subscribers] + summary: Unsubscribe from all mailings + x-slate: + order: 4 + intro: "To unsubscribe a subscriber from all mailings:" + response: + intro: "The response looks like this:" + arguments: none + responses: + "200": + description: The unsubscribed subscriber. The `subscribers` property is an array of one object. + content: + application/json: + schema: + type: object + required: [subscribers] + properties: + links: + type: object + description: Top-level link data included with all responses containing subscriber data. + properties: + "subscribers.account": + type: string + subscribers: + type: array + description: An array of one subscriber object. + items: + $ref: "#/components/schemas/Subscriber" + example: + links: + subscribers.account: "https://api.getdrip.com/v2/accounts/{subscribers.account}" + subscribers: + - id: z1togz2hcjrkpp5treip + status: unsubscribed + email: john@acme.com + first_name: John + last_name: Doe + address1: 123 Main St. + address2: Suite 200 + city: Los Angeles + state: CA + zip: "90210" + country: US + phone: 555-555-5555 + sms_number: "+16125551212" + sms_consent: true + eu_consent: granted + time_zone: America/Los_Angeles + utc_offset: -440 + visitor_uuid: sa8f7sdf78sdsdahf788d7asf8sd + custom_fields: + shirt_size: Medium + tags: [Customer, SEO] + ip_address: 111.111.111.11 + user_agent: Chrome/36.0.1985.143 + original_referrer: "http://www.getdrip.com" + landing_url: "https://www.getdrip.com/docs/rest-api" + prospect: true + lead_score: 72 + lifetime_value: 10000 + created_at: "2013-06-21T10:31:58Z" + href: "https://api.getdrip.com/v2/9999999/subscribers/12345" + user_id: "12345" + base_lead_score: 30 + links: + account: "9999999" + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl -X POST "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/subscribers/ID_OR_EMAIL/unsubscribe_all" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + subscriber_email = "john@acme.com" + response = client.unsubscribe_from_all(subscriber_email) + + if response.success? + puts response.body["subscribers"] + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const idOrEmail = "someone@example.com"; + + client.unsubscribeFromAllMailings(idOrEmail) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + +components: + schemas: + Subscriber: + type: object + description: | + All subscriber API endpoints only work with your + [active](https://www.drip.com/learn/docs/manual/people/active) people. + Attempting to modify or delete an + [inactive](https://www.drip.com/learn/docs/manual/people/inactive) + person will result in an error. + example: + id: z1togz2hcjrkpp5treip + status: active + email: john@acme.com + first_name: John + last_name: Doe + address1: 123 Main St. + address2: Suite 200 + city: Los Angeles + state: CA + zip: "90210" + country: US + phone: 555-555-5555 + sms_number: "+16125551212" + sms_consent: true + eu_consent: granted + time_zone: America/Los_Angeles + utc_offset: -440 + visitor_uuid: sa8f7sdf78sdsdahf788d7asf8sd + custom_fields: + shirt_size: Medium + tags: [Customer, SEO] + ip_address: 111.111.111.11 + user_agent: Chrome/36.0.1985.143 + original_referrer: "http://www.getdrip.com" + landing_url: "https://www.getdrip.com/docs/rest-api" + prospect: true + lead_score: 72 + lifetime_value: 10000 + created_at: "2013-06-21T10:31:58Z" + href: "https://api.getdrip.com/v2/9999999/subscribers/12345" + user_id: "12345" + base_lead_score: 30 + links: + account: "9999999" + properties: + id: + type: string + description: A read-only Drip generated unique id used to identify each subscriber record. + status: + type: string + enum: [active, unsubscribed] + description: The subscriber's status whether `active` or `unsubscribed`. + initial_status: + type: string + enum: [active, unsubscribed] + description: The subscriber's known status whether `active` or `unsubscribed`. To be used instead of `status` if subscriber's status is unchanged. + email: + type: string + format: email + description: The subscriber's email address. + first_name: + type: string + description: The subscriber's first name. + last_name: + type: string + description: The subscriber's last name. + address1: + type: string + description: The subscriber's mailing address. + address2: + type: string + description: An additional field for the subscriber's mailing address. + city: + type: string + description: The city, town, or village in which the subscriber resides. + state: + type: string + description: The region in which the subscriber resides. Typically a province, a state, or a prefecture. + zip: + type: string + description: The postal code in which the subscriber resides, also known as zip, postcode, Eircode, etc. + country: + type: string + description: The country in which the subscriber resides. + phone: + type: string + description: The subscriber's primary phone number. + sms_number: + type: string + description: Optional. String. The subscriber's mobile phone number in E.164 formatting. E.g. `"+16125551212"`. Only US-based numbers are supported at this time. + sms_consent: + type: boolean + default: false + description: >- + Optional. Boolean. `true` if the person has granted consent to + receive marketing and other communication via SMS; `false` + otherwise. Default: false. If you’re unsure whether or not you + have gained legal SMS consent, check out our + [TCPA requirements article](https://my.drip.com/docs/manual/sms/compliance-manage-sms-compliance). + eu_consent: + type: string + enum: [granted, denied, unknown] + description: A string describing whether the subscriber GDPR consent is `granted`, `denied`, or `unknown`. + time_zone: + type: string + description: The subscriber's time zone (in Olson format). + utc_offset: + type: integer + description: The UTC offset in minutes relative to UTC/GMT. + visitor_uuid: + type: string + description: A read-only Drip generated unique id used to identify each subscriber's visitor record if available. + custom_fields: + type: object + additionalProperties: true + description: >- + An Object containing custom field data. E.g. `{ "shirt_size": "Medium" }`. + tags: + type: array + items: + type: string + description: An Array containing one or more tags. E.g. `["Customer", "SEO"]`. + ip_address: + type: string + description: The subscriber's IP address if available. + user_agent: + type: string + description: The subscriber's browser User Agent if available. + original_referrer: + type: string + description: The initial referral URL when the subscriber first visited your site if available. + landing_url: + type: string + description: The first page visited by the subscriber if available. + prospect: + type: boolean + description: Returns true if the subscriber is marked as a lead. + lead_score: + type: integer + description: The subscriber's lead score. + lifetime_value: + type: integer + description: The subscriber's lifetime value in cents. + created_at: + type: string + format: date-time + description: A timestamp representing when the subscriber record was first created. + href: + type: string + description: The url designated for retrieving the subscriber record via the REST API. + user_id: + type: string + description: A unique identifier for the user in your database, such as a primary key. + base_lead_score: + type: integer + default: 30 + description: An Integer specifying the starting value for lead score calculation for this subscriber. Defaults to `30`. + links: + type: object + description: An object containing the REST API URL for the account. + properties: + account: + type: string + + SubscriberRequest: + type: object + description: Either `email` or `id` or `visitor_uuid` must be included. + properties: + email: + type: string + format: email + description: The subscriber's email address. Either `email` or `id` or `visitor_uuid` must be included. + id: + type: string + description: The subscriber's Drip `id`. Either `email` or `id` or `visitor_uuid` must be included. + visitor_uuid: + type: string + description: The `uuid` for a subscriber's visitor record. Either `email` or `id` or `visitor_uuid` must be included. + new_email: + type: string + format: email + description: A new email address for the subscriber. If provided and a subscriber with the `email` above does not exist, this address will be used to create a new subscriber. + first_name: + type: string + description: The subscriber's first name. + last_name: + type: string + description: The subscriber's last name. + address1: + type: string + description: The subscriber's mailing address. + address2: + type: string + description: An additional field for the subscriber's mailing address. + city: + type: string + description: The city, town, or village in which the subscriber resides. + state: + type: string + description: The region in which the subscriber resides. Typically a province, a state, or a prefecture. + zip: + type: string + description: The postal code in which the subscriber resides, also known as zip, postcode, Eircode, etc. + country: + type: string + description: The country in which the subscriber resides. + phone: + type: string + description: The subscriber's primary phone number. + sms_number: + type: string + description: String. The subscriber's mobile phone number in E.164 formatting. E.g. `"+16125551212"`. Only US-based numbers are supported at this time. + sms_consent: + type: boolean + default: false + description: >- + Boolean. `true` if the person has granted consent to receive + marketing and other communication via SMS; `false` otherwise. + Default: false. If you’re unsure whether or not you have gained + legal SMS consent, check out our + [TCPA requirements article](https://my.drip.com/docs/manual/sms/compliance-manage-sms-compliance). + user_id: + type: string + description: A unique identifier for the user in your database, such as a primary key. + time_zone: + type: string + default: Etc/UTC + description: The subscriber's time zone (in Olson format). Defaults to `Etc/UTC` + lifetime_value: + type: integer + description: The lifetime value of the subscriber (in cents). + ip_address: + type: string + description: The subscriber's ip address E.g. `"111.111.111.11"` + custom_fields: + type: object + additionalProperties: true + description: >- + An Object containing custom field data. E.g. `{ "shirt_size": "Medium" }`. + tags: + type: array + items: + type: string + description: An Array containing one or more tags. E.g. `["Customer", "SEO"]`. + remove_tags: + type: array + items: + type: string + description: An Array containing one or more tags to be removed from the subscriber. E.g. `["Customer", "SEO"]`. + prospect: + type: boolean + default: true + description: A Boolean specifiying whether we should attach a lead score to the subscriber (when lead scoring is enabled). Defaults to `true`. **Note:** This flag used to be called `potential_lead`, which we will continue to accept for backwards compatibility. + base_lead_score: + type: integer + default: 30 + description: An Integer specifying the starting value for lead score calculation for this subscriber. Defaults to `30`. + eu_consent: + type: string + enum: [granted, denied] + description: A String specifying whether the subscriber `granted` or `denied` GDPR consent. + eu_consent_message: + type: string + description: A String containing the message the subscriber granted or denied their consent to. + status: + type: string + enum: [active, unsubscribed] + description: >- + A String specifying the subscriber's status: either `active` or + `unsubscribed`. + initial_status: + type: string + enum: [active, unsubscribed] + description: >- + A String specifying the subscriber's known status: either `active` + or `unsubscribed`. To be used if subscriber's status is unchanged. diff --git a/spec/fragments/tags.yaml b/spec/fragments/tags.yaml new file mode 100644 index 00000000000..e1330c74997 --- /dev/null +++ b/spec/fragments/tags.yaml @@ -0,0 +1,298 @@ +tags: + - name: Tags + description: Tags applied to subscribers in an account. + +paths: + /{account_id}/tags: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + + get: + operationId: listTags + tags: [Tags] + summary: List all tags used in an account + x-slate: + intro: "To list all tags:" + response: + intro: "The response looks like this:" + # Anomaly carried from the published page: the request-field table for + # `apply tag` is shown here under the GET. Kept verbatim for parity. + sections: | + **Properties** + + + + + + + + + + + + + + + + + + +
PropertyDescription
emailThe subscriber's email address.
tagThe String tag to apply. E.g. "Customer".
+ arguments: none + responses: + "200": + description: All tags used in the account. + content: + application/json: + schema: + type: object + required: [tags] + properties: + tags: + type: array + description: The list of tags used in the account. + items: + type: string + example: + tags: + - Customer + - SEO + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/tags" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + response = client.tags + + if response.success? + puts response.body["tags"] + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + + client.listAllTags() + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + + post: + operationId: applyTags + tags: [Tags] + summary: Apply a tag to a subscriber + x-slate: + intro: "To apply a tag to a specific subscriber:" + response: + intro: "Responds with a `201 Created` and an empty JSON response:" + # Published page drops the trailing period on the tag description here + # (it keeps it in the Properties table above). Kept verbatim for parity. + arguments: | + + + + + + + + + + + + + + + + + +
KeyDescription
emailThe subscriber's email address.
tagThe String tag to apply. E.g. "Customer"
+ requestBody: + required: true + content: + application/json: + schema: + type: object + required: [tags] + properties: + tags: + type: array + items: + $ref: "#/components/schemas/TagApplication" + example: + tags: + - email: john@acme.com + tag: Customer + responses: + "201": + description: Created. Responds with an empty JSON object. + content: + application/json: + schema: + type: object + example: {} + "422": + $ref: "../components.yaml#/components/responses/ValidationError" + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl -X POST "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/tags" \ + -H "Content-Type: application/json" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: \ + -d @- << EOF + { + "tags": [{ + "email": "john@acme.com", + "tag": "Customer" + }] + } + EOF + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + subscriber_email = "john@acme.com" + tag = "Customer" + + response = client.apply_tag(subscriber_email, tag) + + if response.success? + # ... + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const payload = { + tags: [{ + email: "john@acme.com", + tag: "Customer" + }] + }; + + client.tagSubscriber(payload) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + + /{account_id}/subscribers/{id_or_email}/tags/{tag}: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + - name: id_or_email + in: path + required: true + description: The subscriber's ID or email address. + schema: + type: string + - name: tag + in: path + required: true + description: The tag to remove. E.g. `Customer`. + schema: + type: string + + delete: + operationId: removeSubscriberTag + tags: [Tags] + summary: Remove a tag from a subscriber + x-slate: + # Published page reuses the "apply" annotation verbatim. Kept for parity. + intro: "To apply a tag to a specific subscriber:" + response: + intro: "Responds with a `204 No Content` if successful." + endpoint: "DELETE /:account_id/subscribers/:email/tags/:tag" + arguments: none + responses: + "204": + description: No Content. The tag was removed. + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl -X DELETE "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/subscribers/ID_OR_EMAIL/tags/TAG" \ + -H "Content-Type: application/json" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + subscriber_email = "john@acme.com" + tag = "Customer" + + response = client.remove_tag(subscriber_email, tag) + + if response.success? + # ... + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const email = "john@acme.com"; + const tag = "Customer"; + + client.removeSubscriberTag(email, tag) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + +components: + schemas: + TagApplication: + type: object + required: [email, tag] + properties: + email: + type: string + format: email + description: The subscriber's email address. + tag: + type: string + description: The String tag to apply. E.g. "Customer". diff --git a/spec/fragments/users.yaml b/spec/fragments/users.yaml new file mode 100644 index 00000000000..b197b24b4a5 --- /dev/null +++ b/spec/fragments/users.yaml @@ -0,0 +1,86 @@ +tags: + - name: Users + description: The user authenticated to the API. + x-slate: + lead: | + > Users are represented as follows: + + ```json + { + "email": "john@acme.com", + "name": "John Doe", + "time_zone": "America/Los_Angeles" + } + ``` +paths: + /user: + get: + operationId: fetchUser + tags: [Users] + summary: Fetch the authenticated user + x-slate: + intro: "To fetch the current user:" + response: + intro: "The response looks like this:" + body: | + { + "users":[{ + "email": "john@acme.com", + "name": "John Doe", + "time_zone": "America/Los_Angeles" + }] + } + arguments: none + responses: + "200": + description: The authenticated user, wrapped in a `users` array. + content: + application/json: + schema: + type: object + required: [users] + properties: + users: + type: array + items: + $ref: "#/components/schemas/User" + example: + users: + - email: john@acme.com + name: John Doe + time_zone: America/Los_Angeles + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl "https://api.getdrip.com/v2/user" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + + client.fetchUser() + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); +components: + schemas: + User: + type: object + properties: + email: + type: string + format: email + name: + type: string + time_zone: + type: string diff --git a/spec/fragments/webhooks.yaml b/spec/fragments/webhooks.yaml new file mode 100644 index 00000000000..be4eff0bb6d --- /dev/null +++ b/spec/fragments/webhooks.yaml @@ -0,0 +1,519 @@ +tags: + - name: Webhooks + description: Webhook subscriptions for event notifications in an account. + x-slate: + lead: | + > Webhooks are represented as follows: + + ```json + { + "id": "77777", + "href": "https://api.getdrip.com/v2/9999999/webhooks/77777", + "post_url": "http://www.mysite.com/my-webhook-endpoint", + "version": "1", + "include_received_email": false, + "events": [ + "subscriber.created", + "subscriber.subscribed_to_campaign" + ], + "created_at": "2013-06-21T10:31:58Z", + "links": { + "account": "9999999", + } + } + ``` + + > All responses containing webhook data also include the following top-level link data: + + ```json + { + "links": { + "webhooks.account": "https://api.getdrip.com/v2/accounts/{webhooks.account}" + } + } + ``` + + **Properties** + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyDescription
idA read-only Drip generated unique id used to identify each webhook record.
hrefThe url designated for retrieving the webhook record via the REST API.
post_urlThe url that the webhook will post to.
versionThe webhook version. The current stable version is 2.
include_received_emailReturns true if a notification is sent whenever a subscriber receives an email.
created_atA timestamp representing when the webhook record was first created.
eventsAn array specifying which events are enabled for webhook notifications.
linksAn object containing the REST API URL for the account.
+ +paths: + /{account_id}/webhooks: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + + get: + operationId: listWebhooks + tags: [Webhooks] + summary: List all webhooks + x-slate: + order: 0 + intro: "To list all webhooks in an account:" + response: + intro: "The response looks like this:" + body: | + # The webhooks property is an array of webhook objects. + { + "links": { ... }, + "webhooks": [ ... ] + } + arguments: none + responses: + "200": + description: The webhooks property is an array of webhook objects. + content: + application/json: + schema: + type: object + required: [webhooks] + properties: + links: + type: object + description: Top-level link data included with all responses containing webhook data. + webhooks: + type: array + description: The list of webhooks in the account. + items: + $ref: "#/components/schemas/Webhook" + example: + links: + webhooks.account: https://api.getdrip.com/v2/accounts/{webhooks.account} + webhooks: + - id: "77777" + href: https://api.getdrip.com/v2/9999999/webhooks/77777 + post_url: http://www.mysite.com/my-webhook-endpoint + version: "1" + include_received_email: false + events: + - subscriber.created + - subscriber.subscribed_to_campaign + created_at: "2013-06-21T10:31:58Z" + links: + account: "9999999" + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/webhooks" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + response = client.webhooks + + if response.success? + puts response.body["webhooks"] + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + + client.listWebhooks() + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + + post: + operationId: createWebhook + tags: [Webhooks] + summary: Create a new webhook + x-slate: + order: 2 + intro: "To create a webhook:" + response: + intro: "Responds with a `201 Created` like this:" + body: | + { + "links": { ... }, + "webhooks": [{ + "id": "77777", + "href": "https://api.getdrip.com/v2/9999999/webhooks/77777", + "post_url": "http://www.example.com/api/v2/form", + "version": "1", + "include_received_email": false, + "events": [ + "subscriber.created", + "subscriber.subscribed_to_campaign" + ], + "created_at": "2013-06-21T10:31:58Z", + "links": { + "account": "9999999", + } + }] + } + arguments: auto + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [webhooks] + properties: + webhooks: + type: array + items: + $ref: "#/components/schemas/NewWebhook" + example: + webhooks: + - post_url: http://www.mysite.com/my-webhook-endpoint + events: + - subscriber.created + - subscriber.subscribed_to_campaign + responses: + "201": + description: Created. The webhooks property is an array of one webhook object. + content: + application/json: + schema: + type: object + required: [webhooks] + properties: + links: + type: object + description: Top-level link data included with all responses containing webhook data. + webhooks: + type: array + description: An array containing the created webhook. + items: + $ref: "#/components/schemas/Webhook" + example: + links: + webhooks.account: https://api.getdrip.com/v2/accounts/{webhooks.account} + webhooks: + - id: "77777" + href: https://api.getdrip.com/v2/9999999/webhooks/77777 + post_url: http://www.example.com/api/v2/form + version: "1" + include_received_email: false + events: + - subscriber.created + - subscriber.subscribed_to_campaign + created_at: "2013-06-21T10:31:58Z" + links: + account: "9999999" + "422": + $ref: "../components.yaml#/components/responses/ValidationError" + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl -X POST "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/webhooks" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: \ + -d @- << EOF + { + "webhooks": [{ + "post_url": "http://www.mysite.com/my-webhook-endpoint", + "events": [ + "subscriber.created", + "subscriber.subscribed_to_campaign" + ] + }] + } + EOF + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + pot_url = "https://www.mylistener.com/receieve" + include_received_email = false + events = [ + "subscriber.created", + "subscriber.subscribed_to_campaign" + ] + + response = client.create_webhook(post_url, include_received_email, events) + + if response.success? + puts response.body["webhooks"] + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const payload = { + webhooks: [{ + post_url: "http://www.mysite.com/my-webhook-endpoint", + events: [ + "subscriber.created", + "subscriber.subscribed_to_campaign" + ] + }] + } + + client.createWebhook(payload) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + + /{account_id}/webhooks/{webhook_id}: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + - name: webhook_id + in: path + required: true + description: The ID of the webhook. + schema: + type: string + + get: + operationId: fetchWebhook + tags: [Webhooks] + summary: Fetch a webhook + x-slate: + order: 1 + intro: "To fetch a specific webhook:" + response: + intro: "The response looks like this:" + body: | + # The webhooks property is an array of one webhook object. + { + "links": { ... }, + "webhooks": [{ ... }] + } + arguments: none + responses: + "200": + description: The webhooks property is an array of one webhook object. + content: + application/json: + schema: + type: object + required: [webhooks] + properties: + links: + type: object + description: Top-level link data included with all responses containing webhook data. + webhooks: + type: array + description: An array of one webhook object. + items: + $ref: "#/components/schemas/Webhook" + example: + links: + webhooks.account: https://api.getdrip.com/v2/accounts/{webhooks.account} + webhooks: + - id: "77777" + href: https://api.getdrip.com/v2/9999999/webhooks/77777 + post_url: http://www.mysite.com/my-webhook-endpoint + version: "1" + include_received_email: false + events: + - subscriber.created + - subscriber.subscribed_to_campaign + created_at: "2013-06-21T10:31:58Z" + links: + account: "9999999" + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/webhooks/WEBHOOK_ID" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + webhook_id = 999999 + response = client.webhook(webhook_id) + + if response.success? + puts response.body["webhooks"] + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const webhookId = 111222; + + client.fetchWebhook(webhookId) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + + delete: + operationId: destroyWebhook + tags: [Webhooks] + summary: Destroy a webhook + x-slate: + order: 3 + intro: "To destroy an existing webhook:" + response: + intro: "Responds with a `204 No Content` if successful." + arguments: none + responses: + "204": + description: No Content. The webhook was destroyed. + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl -X DELETE "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/webhooks/WEBHOOK_ID" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + webhook_id = 999999 + response = client.delete_webhook(webhook_id) + + if response.success? + # ... + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const webhookId = 111222; + + client.destroyWebhook(webhookId) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + +components: + schemas: + Webhook: + type: object + properties: + id: + type: string + description: A read-only Drip generated unique id used to identify each webhook record. + href: + type: string + description: The url designated for retrieving the webhook record via the REST API. + post_url: + type: string + description: The url that the webhook will post to. + version: + type: string + description: The webhook version. The current stable version is 2. + include_received_email: + type: boolean + description: Returns true if a notification is sent whenever a subscriber receives an email. + events: + type: array + description: An array specifying which events are enabled for webhook notifications. + items: + type: string + created_at: + type: string + description: A timestamp representing when the webhook record was first created. + links: + type: object + description: An object containing the REST API URL for the account. + + NewWebhook: + type: object + required: [post_url] + properties: + post_url: + type: string + description: The url that the webhook will post to. + include_received_email: + type: boolean + description: Optional. A Boolean specifying whether we should send a notification whenever a subscriber receives an email. Defaults to `false`. + events: + type: array + description: Optional. An Array specifiying which events we should send notifications for. Eligible events can be found in the [webhooks documentation](#webhook-events). By default, we will send notifications for all events except `subscrber.received_email`. + items: + type: string diff --git a/spec/fragments/workflows.yaml b/spec/fragments/workflows.yaml new file mode 100644 index 00000000000..2dbbcd1a5b9 --- /dev/null +++ b/spec/fragments/workflows.yaml @@ -0,0 +1,1058 @@ +tags: + - name: Workflows + description: Automation workflows in an account, with draft, active, or paused statuses. + x-slate: + represent: + - intro: "Workflows are represented as follows:" + schema: Workflow + - intro: "All responses containing workflow data also include the following top-level link data:" + json: | + { + "links": { + "workflows.account": "https://api.getdrip.com/v2/accounts/{workflows.account}" + } + } + properties: Workflow + +paths: + /{account_id}/workflows: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + + get: + operationId: listWorkflows + tags: [Workflows] + summary: List all workflows + x-slate: + intro: "To list all workflows:" + response: + intro: "The response looks like this:" + arguments: auto + parameters: + - name: status + in: query + required: false + description: >- + Filter by one of the following statuses: `draft`, `active`, or + `paused`. Defaults to `all`. + schema: + type: string + enum: [all, draft, active, paused] + default: all + - name: sort + in: query + required: false + description: >- + Sort results by one of these fields: `created_at` or `name`. + Defaults to `created_at`. + schema: + type: string + enum: [created_at, name] + default: created_at + - name: direction + in: query + required: false + description: >- + Filter sort direction with: `asc` or `desc`. Defaults to `asc`. + schema: + type: string + enum: [asc, desc] + default: asc + responses: + "200": + description: All workflows in the account. + content: + application/json: + schema: + type: object + properties: + links: + type: object + description: Top-level link data. + meta: + type: object + description: Pagination metadata. + properties: + page: + type: integer + sort: + type: string + direction: + type: string + count: + type: integer + total_pages: + type: integer + total_count: + type: integer + status: + type: string + workflows: + type: array + description: An array of workflow objects. + items: + $ref: "#/components/schemas/Workflow" + example: + links: + workflows.account: "https://api.getdrip.com/v2/accounts/{workflows.account}" + meta: + page: 1 + sort: sort_order + direction: desc + count: 5 + total_pages: 1 + total_count: 5 + status: all + workflows: + - id: "123456" + href: "https://api.getdrip.com/v2/9999999/workflows/123456" + name: Main Funnel + status: active + created_at: "2016-07-01T10:00:00Z" + links: + account: "9999999" + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/workflows" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + response = client.workflows + + if response.success? + puts response.body["workflows"] + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const options = { status: "active" }; + + client.listAllWorkflows(options) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + + /{account_id}/workflows/{workflow_id}: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + - name: workflow_id + in: path + required: true + description: The ID of the workflow. + schema: + type: string + + get: + operationId: fetchWorkflow + tags: [Workflows] + summary: Fetch a workflow + x-slate: + intro: "To fetch a workflow:" + response: + intro: "The response looks like this:" + arguments: none + responses: + "200": + description: The workflows property is an array of one workflow object. + content: + application/json: + schema: + type: object + properties: + links: + type: object + description: Top-level link data. + workflows: + type: array + description: An array of one workflow object. + items: + $ref: "#/components/schemas/Workflow" + example: + links: + workflows.account: "https://api.getdrip.com/v2/accounts/{workflows.account}" + workflows: + - id: "123456" + href: "https://api.getdrip.com/v2/9999999/workflows/123456" + name: Main Funnel + status: active + created_at: "2016-07-01T10:00:00Z" + links: + account: "9999999" + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/workflows/WORKFLOW_ID" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + workflow_id = 9999999 + response = client.workflow(workflow_id) + + if response.success? + puts response.body["workflows"] + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const workflowId = 222333; + + client.fetchWorkflow(workflowId) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + + /{account_id}/workflows/{workflow_id}/activate: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + - name: workflow_id + in: path + required: true + description: The ID of the workflow. + schema: + type: string + + post: + operationId: activateWorkflow + tags: [Workflows] + summary: Activate a workflow + x-slate: + intro: "To activate a workflow:" + response: + intro: "Responds with a `204 No Content` if successful." + arguments: none + responses: + "204": + description: No Content. The workflow was activated. + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl -X POST "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/workflows/WORKFLOW_ID/activate" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + workflow_id = 9999999 + response = client.activate_workflow(workflow_id) + + if response.success? + # ... + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const workflowId = 222333; + + client.activateWorkflow(workflowId) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + + /{account_id}/workflows/{workflow_id}/pause: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + - name: workflow_id + in: path + required: true + description: The ID of the workflow. + schema: + type: string + + post: + operationId: pauseWorkflow + tags: [Workflows] + summary: Pause a workflow + x-slate: + intro: "To pause a workflow:" + response: + intro: "Responds with a `204 No Content` if successful." + arguments: none + responses: + "204": + description: No Content. The workflow was paused. + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl -X POST "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/workflows/WORKFLOW_ID/pause" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + workflow_id = 9999999 + response = client.pause_workflow(workflow_id) + + if response.success? + # ... + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const workflowId = 222333; + + client.pauseWorkflow(workflowId) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + + /{account_id}/workflows/{workflow_id}/subscribers: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + - name: workflow_id + in: path + required: true + description: The ID of the workflow. + schema: + type: string + + post: + operationId: startSubscriberOnWorkflow + tags: [Workflows] + summary: Start someone on a workflow + description: >- + If the workflow is not active, the subscriber will not be added to + the workflow. + x-slate: + intro: "To start someone on a workflow:" + response: + intro: "The response looks like this:" + show-description: true + arguments: auto + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [subscribers] + properties: + subscribers: + type: array + description: An array containing one subscriber object. + items: + $ref: "#/components/schemas/WorkflowSubscriber" + example: + subscribers: + - email: john@acme.com + time_zone: America/Los_Angeles + custom_fields: + shirt_size: Medium + responses: + "200": + description: The subscribers property is an array of one object. + content: + application/json: + schema: + type: object + properties: + links: + type: object + description: Top-level link data. + subscribers: + type: array + description: >- + An array of one subscriber object (documented under the + Subscribers resource; not defined here). + items: + type: object + "422": + $ref: "../components.yaml#/components/responses/ValidationError" + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl -X POST "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/workflows/WORKFLOW_ID/subscribers" \ + -H "Content-Type: application/json" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: \ + -d @- << EOF + { + "subscribers": [{ + "email": "john@acme.com", + "time_zone": "America/Los_Angeles", + "custom_fields": { + "shirt_size": "Medium" + } + }] + } + EOF + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + workflow_id = 9999999 + options = { + email: "john@acme.com", + time_zone: "America/Los_Angeles", + custom_fields: { + shirt_size: "Medium" + } + } + response = client.start_subscriber_workflow(workflow_id, options) + + if response.success? + puts response.body["subscribers"] + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const workflowId = 222333; + const payload = { + subscribers: [{ + email: "john@acme.com", + time_zone: "America/Los_Angeles", + custom_fields: { + shirt_size: "Medium" + } + }] + } + + client.startOnWorkflow(workflowId, payload) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + + /{account_id}/workflows/{workflow_id}/subscribers/{id_or_email}: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + - name: workflow_id + in: path + required: true + description: The ID of the workflow. + schema: + type: string + - name: id_or_email + in: path + required: true + description: The subscriber's ID or email address. + schema: + type: string + + delete: + operationId: removeSubscriberFromWorkflow + tags: [Workflows] + summary: Remove a subscriber from a workflow + description: >- + If the subscriber is not already on the workflow, nothing will happen. + x-slate: + intro: "To remove someone from a workflow:" + response: + intro: "Responds with a `204 No Content` if successful." + show-description: true + arguments: none + responses: + "204": + description: No Content. The subscriber was removed from the workflow. + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl -X DELETE "https://v2/api.getdrip.com/v2/YOUR_ACCOUNT_ID/workflows/WORKFLOW_ID/subscribers/ID_OR_EMAIL" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + workflow_id = 9999999 + subscriber_email = "john@acme.com" + + response = client.remove_subscriber_workflow(workflow_id, subscriber_email) + + if response.success? + # ... + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const workflowId = 222333; + const idOrEmail = "someone@example.com" + + client.removeFromWorkflow(workflowId, idOrEmail) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + + /{account_id}/workflows/{workflow_id}/triggers: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + - name: workflow_id + in: path + required: true + description: The ID of the workflow. + schema: + type: string + + get: + operationId: listWorkflowTriggers + tags: [Workflows] + summary: List all workflow triggers + x-slate: + intro: "To list all triggers on a workflow:" + response: + intro: "The response looks like this:" + arguments: none + responses: + "200": + description: All triggers on the workflow. + content: + application/json: + schema: + type: object + properties: + triggers: + type: array + items: + $ref: "#/components/schemas/Trigger" + example: + triggers: + - id: f7gysdf7gyd7 + type: trigger + trigger_type: submitted_landing_page + provider: leadpages + properties: + landing_page: My Landing Page + actions_required: + - code: configure_provider + message: Configure your LeadPages connection + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/workflows/WORKFLOW_ID/triggers" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + workflow_id = 9999999 + response = client.workflow_triggers(workflow_id) + + if response.success? + puts response.body["triggers"] + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const workflowId = 222333; + + client.listTriggers(workflowId) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + + post: + operationId: createWorkflowTrigger + tags: [Workflows] + summary: Create a workflow trigger + x-slate: + intro: "To create a workflow trigger:" + response: + intro: "The response looks like this:" + arguments: auto + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [triggers] + properties: + triggers: + type: array + items: + $ref: "#/components/schemas/TriggerRequest" + example: + triggers: + - provider: leadpages + trigger_type: submitted_landing_page + properties: + landing_page: My Landing Page + responses: + "200": + description: The created trigger. + content: + application/json: + schema: + type: object + properties: + triggers: + type: array + items: + $ref: "#/components/schemas/Trigger" + example: + triggers: + - id: f7gysdf7gyd7 + type: trigger + provider: leadpages + trigger_type: submitted_landing_page + properties: + landing_page: My Landing Page + actions_required: + - code: configure_provider + message: Configure your LeadPages connection + "422": + $ref: "../components.yaml#/components/responses/ValidationError" + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl -X POST "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/workflows/WORKFLOW_ID/triggers" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: \ + -d @- << EOF + { + "triggers": [ + { + "provider": "leadpages", + "trigger_type": "submitted_landing_page", + "properties": { + "landing_page": "My Landing Page" + } + } + ] + } + EOF + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + workflow_id = 9999999 + options = { + provider: "leadpages", + trigger_type: "submitted_landing_page", + properties: { + landing_page: "My Landing Page" + } + } + response = client.create_workflow_trigger(workflow_id, options) + + if response.success? + puts response.body["triggers"] + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const workflowId = 222333; + const payload = { + provider: "leadpages", + trigger_type: "submitted_landing_page", + properties: { + landing_page: "My Landing Page" + } + } + + client.createTrigger(workflowId, payload) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + + /{account_id}/workflows/{workflow_id}/triggers/{trigger_id}: + parameters: + - $ref: "../components.yaml#/components/parameters/accountId" + - name: workflow_id + in: path + required: true + description: The ID of the workflow. + schema: + type: string + - name: trigger_id + in: path + required: true + description: The ID of the trigger. + schema: + type: string + + put: + operationId: updateWorkflowTrigger + tags: [Workflows] + summary: Update a workflow trigger + x-slate: + intro: "To update a workflow trigger:" + response: + intro: "The response looks like this:" + arguments: auto + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [triggers] + properties: + triggers: + type: array + items: + $ref: "#/components/schemas/TriggerRequest" + example: + triggers: + - provider: leadpages + trigger_type: submitted_landing_page + properties: + landing_page: My Landing Page + responses: + "200": + description: The updated trigger. + content: + application/json: + schema: + type: object + properties: + triggers: + type: array + items: + $ref: "#/components/schemas/Trigger" + example: + triggers: + - id: f7gysdf7gyd7 + type: trigger + provider: leadpages + trigger_type: submitted_landing_page + properties: + landing_page: My Landing Page + actions_required: + - code: configure_provider + message: Configure your LeadPages connection + "422": + $ref: "../components.yaml#/components/responses/ValidationError" + "4XX": + $ref: "../components.yaml#/components/responses/Error" + x-codeSamples: + - lang: shell + label: cURL + source: | + curl -X PUT "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/workflows/WORKFLOW_ID/triggers/TRIGGER_ID" \ + -H 'User-Agent: Your App Name (www.yourapp.com)' \ + -u YOUR_API_KEY: \ + -d @- << EOF + { + "triggers": [ + { + "provider": "leadpages", + "trigger_type": "submitted_landing_page", + "properties": { + "landing_page": "My Landing Page" + } + } + ] + } + EOF + - lang: ruby + label: Ruby + source: | + require 'drip' + + client = Drip::Client.new do |c| + c.api_key = "YOUR API KEY" + c.account_id = "YOUR_ACCOUNT_ID" + end + + workflow_id = 9999999 + options = { + provider: "leadpages", + trigger_type: "submitted_landing_page", + properties: { + landing_page: "My Landing Page" + } + } + response = client.update_workflow_trigger(workflow_id, options) + + if response.success? + puts response.body["triggers"] + end + - lang: javascript + label: JavaScript + source: | + // npm install drip-nodejs --save + + const client = require('drip-nodejs')({ token: YOUR_API_KEY, accountId: YOUR_ACCOUNT_ID }); + const workflowId = 222333; + const triggerId = "abc123"; + const payload = { + provider: "leadpages", + trigger_type: "submitted_landing_page", + properties: { + landing_page: "My Landing Page" + } + } + + client.updateTrigger(workflowId, triggerId, payload) + .then((response) => { + // Handle `response.body` + }) + .catch((error) => { + // Handle errors + }); + +components: + schemas: + Workflow: + type: object + example: + id: "123456" + href: "https://api.getdrip.com/v2/9999999/workflows/123456" + name: Main Funnel + status: active + created_at: "2016-07-01T10:00:00Z" + links: + account: "9999999" + properties: + id: + type: string + description: A read-only Drip generated unique id used to identify each workflow record. + href: + type: string + description: The url designated for retrieving the workflow record via the REST API. + name: + type: string + description: The name assigned to the workflow. + status: + type: string + description: The workflow's status whether `draft`, `active`, or `paused`. + enum: [draft, active, paused] + created_at: + type: string + format: date-time + description: A timestamp representing when the workflow record was first created. + links: + type: object + description: An object containing the REST API URL for the account. + properties: + account: + type: string + + WorkflowSubscriber: + type: object + description: >- + A subscriber to start on the workflow. Either `email` or `id` must + be included. + properties: + email: + type: string + format: email + description: The subscriber's email address. Either `email` or `id` must be included. + id: + type: string + description: The subscriber's Drip `id`. Either `email` or `id` must be included. + first_name: + type: string + description: The subscriber's first name. + last_name: + type: string + description: The subscriber's last name. + address1: + type: string + description: The subscriber's mailing address. + address2: + type: string + description: An additional field for the subscriber's mailing address. + city: + type: string + description: The city, town, or village in which the subscriber resides. + state: + type: string + description: The region in which the subscriber resides. Typically a province, a state, or a prefecture. + zip: + type: string + description: The postal code in which the subscriber resides, also known as zip, postcode, Eircode, etc. + country: + type: string + description: The country in which the subscriber resides. + phone: + type: string + description: The subscriber's primary phone number. + user_id: + type: string + description: A unique identifier for the user in your database, such as a primary key. + time_zone: + type: string + description: The subscriber's time zone (in Olson format). Defaults to `Etc/UTC`. + default: Etc/UTC + custom_fields: + type: object + description: 'An Object containing custom field data. E.g. `{ "shirt_size": "Medium" }`.' + tags: + type: array + description: 'An Array containing one or more tags. E.g. `["Customer", "SEO"]`.' + items: + type: string + prospect: + type: boolean + description: >- + A Boolean specifiying whether we should attach a lead score to + the subscriber (when lead scoring is enabled). Defaults to + `true`. **Note:** This flag used to be called `potential_lead`, + which we will continue to accept for backwards compatibility. + default: true + eu_consent: + type: string + description: A String specifying whether the subscriber `granted` or `denied` GDPR consent. + eu_consent_message: + type: string + description: A String containing the message the subscriber granted or denied their consent to. + + Trigger: + type: object + properties: + id: + type: string + type: + type: string + provider: + type: string + description: A String indicating a provider. + trigger_type: + type: string + description: A String indicating the automation trigger type. + properties: + type: object + description: An Object containing properties for the given trigger. + actions_required: + type: array + items: + type: object + properties: + code: + type: string + message: + type: string + + TriggerRequest: + type: object + required: [provider, trigger_type] + properties: + provider: + type: string + description: A String indicating a provider. + trigger_type: + type: string + description: A String indicating the automation trigger type. + properties: + type: object + description: An Object containing properties for the given trigger. diff --git a/spec/rest-v2.yaml b/spec/rest-v2.yaml new file mode 100644 index 00000000000..34553480083 --- /dev/null +++ b/spec/rest-v2.yaml @@ -0,0 +1,161 @@ +openapi: 3.1.0 + +# Aggregator / root document for the Drip REST API. +# +# This file stitches the per-resource fragments in spec/fragments/ into a +# single OpenAPI document for the Redoc preview (`redocly preview-docs` / +# `redocly bundle`). Each path is a $ref into its fragment; shared components +# live in spec/components.yaml. +# +# The public Slate docs (source/includes/rest/_*.md) are generated per fragment +# by script/generate-docs. See spec/README.md for the full workflow. + +info: + title: Drip REST API + version: "2" + description: | + The REST API communicates exclusively in JSON over SSL (HTTPS). All endpoint + URLs begin with `https://api.getdrip.com/`. The REST API requires the use of + a client which supports [SNI](https://en.wikipedia.org/wiki/Server_Name_Indication). + + Parameters must be serialized in JSON and passed in the request body (not in + the query string or form parameters), with a media type of `application/json`. + + All requests should include a `User-Agent` header identifying your + application, e.g. `Your App Name (www.yourapp.com)`. + contact: + url: https://developer.drip.com + +servers: + - url: https://api.getdrip.com/v2 + +security: + - apiKey: [] + - oauth2: [] + +# Tag order mirrors the resource order on developer.drip.com +# (source/index.html.md `includes_rest_api`). +tags: + - name: Batch API + description: | + Batch endpoints for creating or updating collections of records in a single + request. Each batch accepts between 1 and 1000 objects. Since the batch APIs + process requests in the background, there may be a delay between the time you + submit your request and the time your data appears in the user interface. + - name: Accounts + description: The Drip accounts the authenticated user has access to. + - name: Broadcasts + description: Single-Email Campaigns (Broadcasts) are one-time emails sent to a segment of your subscribers. + - name: Campaigns + description: Email Series Campaigns in an account. + - name: Custom Fields + description: Custom field identifiers used in an account. + - name: Conversions + description: Conversion goals tracked in an account. See the Events API for recording conversion events. + - name: Events + description: Custom events recorded for subscribers in an account. + - name: Forms + description: Form widgets and embedded forms in an account. + - name: Orders + description: Legacy endpoint for recording orders. Superseded by the Order Activity endpoint, but it will continue to function, with fair notice before retirement. + - name: Subscribers + description: People in an account. All subscriber API endpoints only work with your active people; attempting to modify or delete an inactive person will result in an error. + - name: Tags + description: Tags applied to subscribers in an account. + - name: Users + description: The user authenticated to the API. + - name: Workflows + description: Automation workflows in an account, with draft, active, or paused statuses. + - name: Webhooks + description: Webhook subscriptions for event notifications in an account. + +components: + securitySchemes: + apiKey: + $ref: "./components.yaml#/components/securitySchemes/apiKey" + oauth2: + $ref: "./components.yaml#/components/securitySchemes/oauth2" + +paths: + "/{account_id}/subscribers/batches": + $ref: "./fragments/batch_api.yaml#/paths/~1{account_id}~1subscribers~1batches" + "/{account_id}/unsubscribes/batches": + $ref: "./fragments/batch_api.yaml#/paths/~1{account_id}~1unsubscribes~1batches" + "/{account_id}/events/batches": + $ref: "./fragments/batch_api.yaml#/paths/~1{account_id}~1events~1batches" + "/{account_id}/orders/batches": + $ref: "./fragments/batch_api.yaml#/paths/~1{account_id}~1orders~1batches" + /accounts: + $ref: "./fragments/accounts.yaml#/paths/~1accounts" + "/accounts/{account_id}": + $ref: "./fragments/accounts.yaml#/paths/~1accounts~1{account_id}" + "/{account_id}/broadcasts": + $ref: "./fragments/broadcasts.yaml#/paths/~1{account_id}~1broadcasts" + "/{account_id}/broadcasts/{broadcast_id}": + $ref: "./fragments/broadcasts.yaml#/paths/~1{account_id}~1broadcasts~1{broadcast_id}" + "/{account_id}/broadcasts/{broadcast_id}/send_test": + $ref: "./fragments/broadcasts.yaml#/paths/~1{account_id}~1broadcasts~1{broadcast_id}~1send_test" + "/{account_id}/campaigns": + $ref: "./fragments/campaigns.yaml#/paths/~1{account_id}~1campaigns" + "/{account_id}/campaigns/{campaign_id}": + $ref: "./fragments/campaigns.yaml#/paths/~1{account_id}~1campaigns~1{campaign_id}" + "/{account_id}/campaigns/{campaign_id}/activate": + $ref: "./fragments/campaigns.yaml#/paths/~1{account_id}~1campaigns~1{campaign_id}~1activate" + "/{account_id}/campaigns/{campaign_id}/pause": + $ref: "./fragments/campaigns.yaml#/paths/~1{account_id}~1campaigns~1{campaign_id}~1pause" + "/{account_id}/campaigns/{campaign_id}/subscribers": + $ref: "./fragments/campaigns.yaml#/paths/~1{account_id}~1campaigns~1{campaign_id}~1subscribers" + "/{account_id}/subscribers/{subscriber_id}/campaign_subscription": + $ref: "./fragments/campaigns.yaml#/paths/~1{account_id}~1subscribers~1{subscriber_id}~1campaign_subscription" + "/{account_id}/custom_field_identifiers": + $ref: "./fragments/custom_fields.yaml#/paths/~1{account_id}~1custom_field_identifiers" + "/{account_id}/goals": + $ref: "./fragments/conversions.yaml#/paths/~1{account_id}~1goals" + "/{account_id}/goals/{conversion_id}": + $ref: "./fragments/conversions.yaml#/paths/~1{account_id}~1goals~1{conversion_id}" + "/{account_id}/events": + $ref: "./fragments/events.yaml#/paths/~1{account_id}~1events" + "/{account_id}/event_actions": + $ref: "./fragments/events.yaml#/paths/~1{account_id}~1event_actions" + "/{account_id}/forms": + $ref: "./fragments/forms.yaml#/paths/~1{account_id}~1forms" + "/{account_id}/forms/{form_id}": + $ref: "./fragments/forms.yaml#/paths/~1{account_id}~1forms~1{form_id}" + "/{account_id}/orders": + $ref: "./fragments/orders.yaml#/paths/~1{account_id}~1orders" + "/{account_id}/refunds": + $ref: "./fragments/orders.yaml#/paths/~1{account_id}~1refunds" + "/{account_id}/subscribers": + $ref: "./fragments/subscribers.yaml#/paths/~1{account_id}~1subscribers" + "/{account_id}/subscribers/{id_or_email}": + $ref: "./fragments/subscribers.yaml#/paths/~1{account_id}~1subscribers~1{id_or_email}" + "/{account_id}/subscribers/{id_or_email}/remove": + $ref: "./fragments/subscribers.yaml#/paths/~1{account_id}~1subscribers~1{id_or_email}~1remove" + "/{account_id}/subscribers/{id_or_email}/unsubscribe_all": + $ref: "./fragments/subscribers.yaml#/paths/~1{account_id}~1subscribers~1{id_or_email}~1unsubscribe_all" + "/{account_id}/tags": + $ref: "./fragments/tags.yaml#/paths/~1{account_id}~1tags" + "/{account_id}/subscribers/{id_or_email}/tags/{tag}": + $ref: "./fragments/tags.yaml#/paths/~1{account_id}~1subscribers~1{id_or_email}~1tags~1{tag}" + /user: + $ref: "./fragments/users.yaml#/paths/~1user" + "/{account_id}/workflows": + $ref: "./fragments/workflows.yaml#/paths/~1{account_id}~1workflows" + "/{account_id}/workflows/{workflow_id}": + $ref: "./fragments/workflows.yaml#/paths/~1{account_id}~1workflows~1{workflow_id}" + "/{account_id}/workflows/{workflow_id}/activate": + $ref: "./fragments/workflows.yaml#/paths/~1{account_id}~1workflows~1{workflow_id}~1activate" + "/{account_id}/workflows/{workflow_id}/pause": + $ref: "./fragments/workflows.yaml#/paths/~1{account_id}~1workflows~1{workflow_id}~1pause" + "/{account_id}/workflows/{workflow_id}/subscribers": + $ref: "./fragments/workflows.yaml#/paths/~1{account_id}~1workflows~1{workflow_id}~1subscribers" + "/{account_id}/workflows/{workflow_id}/subscribers/{id_or_email}": + $ref: "./fragments/workflows.yaml#/paths/~1{account_id}~1workflows~1{workflow_id}~1subscribers~1{id_or_email}" + "/{account_id}/workflows/{workflow_id}/triggers": + $ref: "./fragments/workflows.yaml#/paths/~1{account_id}~1workflows~1{workflow_id}~1triggers" + "/{account_id}/workflows/{workflow_id}/triggers/{trigger_id}": + $ref: "./fragments/workflows.yaml#/paths/~1{account_id}~1workflows~1{workflow_id}~1triggers~1{trigger_id}" + "/{account_id}/webhooks": + $ref: "./fragments/webhooks.yaml#/paths/~1{account_id}~1webhooks" + "/{account_id}/webhooks/{webhook_id}": + $ref: "./fragments/webhooks.yaml#/paths/~1{account_id}~1webhooks~1{webhook_id}"