NAV
ruby shell

Introduction

Heads up! Most of the Ruby examples use the scripted_client RubyGem. To get started using the Gem, check out the docs!

Welcome to the Scripted API. As far as we know, it’s the only way to programatically and scaleably generate original, creative written content.

This API follows RESTful conventions and communicates in JSON. One caveat before we begin: due to the nature of content, this API is kind of asynchronous. You can post a job synchronously, but you’ll have to come back 3-5 days later to retrieve the written content. If you need us to fire webhooks upon events (e.g. - “your content is ready for review”) please get in touch!

Authentication

Don’t forget to replace abcd1234 with your Organization Key and abcdefghij0123456789 with your Token!

require 'net/http'
require 'json'

uri = URI.parse("https://api.scripted.com/abcd1234/v1/industries")
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
  request = Net::HTTP::Get.new(uri)
  request["Content-Type"] = "application/json"
  request["Authorization"] = "Bearer abcdefghij0123456789"
  response = http.request request
  parsed = JSON.parse(response.body)
end
curl -H "Authorization: Bearer abcdefghij0123456789" \
    https://api.scripted.com/abcd1234/v1/industries

We use two factors to authenticate requests, an Organization Key and a JSON Web Token. You can find both of them in your Scripted dashboard by navigating to Account Settings and clicking on the API tab. All requests are namespaced under the relevant Organization Key, for example:

GET /abcd1234/v1/jobs

Tokens are passed as a Bearer in the Authorization header, like so:

Bearer abcdefghij0123456789

All requests must be made over HTTPS.

Errors

Errors are returned in an Array like so:


    {
      "errors": [
        "Topic is too long (maximum is 255 characters)",
        "Quantity must be one of the ContentFormat's quantity_options"
      ]
    }

The Scripted API does not always return a 200 OK status.

Status Meaning
400 Bad Request – The request was malformed in some way.
401 Unauthorized – The Token provided was invalid or expired.
403 Forbidden – The Token provided does not have access to the namespace’s Organization Key.
404 Not Found – The specified resource could not be found
500 Internal Server Error – We had a problem with our server. Try again later.
503 Service Unavailable – We’re temporarially offline for maintanance. Please try again later.

Jobs

Sample Job

{
  "id": "5654ec06a6e02a37e7000318",
  "topic": "Where to buy an Orangutan",
  "state": "copyediting",
  "quantity": 1,
  "delivery": "standard",
  "deadline_at": "2015-12-04T01:30:00Z",
  "created_at": "2015-11-24T23:00:22Z",
  "pitch": null,
  "content_format": {
    "id": "5654ec02a6e02a37e70000d5",
    "name": "Standard Blog Post",
    "pitchable": true,
    "length_metric": "350-450 words",
    "quantity_options": [
      1
    ]
  },
  "pricing": {
    "total": 9900
  },
  "writer": {
    "id": "5654ec01a6e02a37e700003b",
    "nickname": "Bob L.",
    "favorite": true
  },
  "document": {
    "id": "5654ec06a6e02a37e700031a",
    "type": "Document"
  },
  "prompts": [
    {
      "id": "5654ec06a6e02a37e700031e",
      "kind": "checkbox",
      "label": "Goal",
      "description": "Select one or many",
      "value": [
        "Informed analysis"
      ],
      "answer_required": false,
      "value_options": [
        "Informed analysis",
        "Thought leadership",
        "Repurpose existing writing",
        "Promote topic",
      ]
    }
  ],
  "guidelines": [
    {
      "id": "5654ebfc93be3b2c623b6e63",
      "name": "Anecdotal",
      "kind": "Tone"
    }
  ],
  "industries": [
    {
      "id": "5654ebfc93be3b2c623b6e2a",
      "name": "Education"
    }
  ]
}

Jobs are at the very center of the Scripted domain model. They represent a request for writing to be fulfilled by one of our freelancers. They are forged from a JobTemplate, and include either a list of Industries or a single Specialty. Specialist jobs are completed by freelancers with more depth of knowledge, so they cost a bit more.

state can be any of the following:

State Meaning
hold Paused by a Scripted employee for further clarification.
awaiting author Submitted to Writers but not yet claimed.
writing In the process of being written.
copyediting Being screened for plagiarism or reviewed by an Editor.
ready for review First draft is ready and awaiting your revision requests.
revising Writer is incorporating your revision requests.
ready for acceptance Final draft is ready. You can either accept or reject.
accepted Accepted and charged.
canceled Canceled and not charged.
rejected Rejected and not charged.

Create a Job

require 'scripted_client'

# First, find a JobTemplate that you'd like to use:

templates = ScriptedClient::JobTemplate.all
blog_post = templates.find { |template| template.name == 'Standard Blog Post' }

# Next, assign some values for the Prompts on that JobTemplate.

key_points = blog_post.prompts.find { |prompt| prompt.label == 'Key Points' }
key_points.value = ['Orangutans make great pets', 'Normal pets are lame']

# Next, you can find an Industry:

industries = ScriptedClient::Industry.all
lifestyle = industries.find { |industry| industry.name == 'Lifestyle & Travel' }

# Now you can create the Job!

job = ScriptedClient::Job.new(
  topic: 'Top 10 Reasons to Buy an Orangutan',
  job_template: blog_post,
  industries: [lifestyle]
)
job.save
# => true
curl -H "Authorization: Bearer abcdefghij0123456789" \
  https://api.scripted.com/abcd1234/v1/jobs \
    -d topic="Why a rug can tie a room together"  \
    -d job_template[id]=5ceb8bb8235bcc76bf475e21 \
    -d job_template[prompts][][id]=52cdda2f8b588f7e02000062 \
    -d job_template[prompts][][value]=Yes \
    -d job_template[prompts][][id]=52cdda2f8b588f7e02000064 \
    -d job_template[prompts][][value]=#apifuntimes \
    -d guidelines[][id]=e9d378cc8e74b4b5faa34892935d5665 \
    -d guidelines[][id]=689ca0246221f2f21e80e9dac9387bce \
    -d industries[][id]=8af7c7ae67b6d0854051378b9f3eede4 \
    -d industries[][id]=92e4132ea0560de3c12f072dc9a81486 \
    -d industries[][id]=f0709aabc7050b3eed7c43db83f0f4cf

POST /:organization_key/v1/jobs

To create a Job, pass a JobTemplate and answers for that JobTemplate’s Prompts. Either a list of Industries or a single Specialty must also be included. Guidelines are optional.

Parameter Description
job_template Required Pick a JobTemplate with a ContentFormat whose pitchable is true.
topic Required What’s this Job all about?
quantity Optional How many do you want? Defaults to minimum of the ContentFormat#quantity_options.
industries Required unless Specialty is passed A list of high-level categories pertaining to your job.
specialty Required unless Industries are passed A deeper field of knowledge.
guidelines Optional Tone, Voice and Perspective.

List all Jobs

ScriptedClient::Job.all

# Or if you want to filter by state

ScriptedClient::Job.draft_ready
curl -H "Authorization: Bearer abcdefghij0123456789" \
    https://api.scripted.com/abcd1234/v1/jobs

# Or if you want to filter by state

curl -H "Authorization: Bearer abcdefghij0123456789" \
    https://api.scripted.com/abcd1234/v1/jobs/draft_ready

GET /:organization_key/v1/jobs

You can pass any of the following filters to narrow your results:

screening writing draft_ready revising final_ready in_progress needs_review accepted rejected finished

See Pagination if you have more than 15 jobs.

Show a Job

Don’t forget to replace 5ceb8bb8235bcc76bf475e21 with the ID of one of your jobs!

ScriptedClient::Job.find('5ceb8bb8235bcc76bf475e21')
curl -H "Authorization: Bearer abcdefghij0123456789" \
    https://api.scripted.com/abcd1234/v1/jobs/5ceb8bb8235bcc76bf475e21

GET /:organization_key/v1/jobs/:id

Get a Job’s HTML Contents

ScriptedClient::Job.find('5ceb8bb8235bcc76bf475e21').html_contents
curl -H "Authorization: Bearer abcdefghij0123456789" \
    https://api.scripted.com/abcd1234/v1/jobs/5ceb8bb8235bcc76bf475e21/html_contents

GET /:organization_key/v1/jobs/:id/html_contents

To retrieve the actual writing, you have to hit a separate endpoint. It returns an Array of HTML Strings.

Pitchsets

Sample Pitchset

{
  "id": "5654ec23a6e02a37e70006f2",
  "tagline": "Cool Ideas for Zoo-centric Content",
  "status": "active",
  "number_of_jobs_requested": 2,
  "number_of_pitches_accepted": 0,
  "number_of_pitches_rejected": 1,
  "number_of_pitches_awaiting_review": 0,
  "specialty": {
    "id": "5654ebfc93be3b2c623b6ea8",
    "name": "Physical Fitness"
  },
  "guidelines": [
    {
      "id": "5654ebfc93be3b2c623b6e60",
      "name": "1st Personal Singular",
      "kind": "Voice"
    }
  ],
  "industries": [],
  "attachments": [
    {
      "id": "568da92bfdcedc00e4000000",
      "name": "analytics.pptx",
      "description": "Use this data in your pitches",
      "kind": "Data",
      "url": "https://zoos.com/analytics.pptx",
      "created_at": "2016-01-06T15:54:19-08:00"
    }
  ],
  "prompts": [
    {
      "id": "5654ec23a6e02a37e70006f6",
      "kind": "string[255]",
      "label": "Target Audience",
      "description": "Describe the particular group at which your content is aimed",
      "value": "Kittens of all types: Frat Kittens, Gamer Kittens, Hackysack Kittens, Tech Kittens, and Kitten Kittens",
      "answer_required": false
    }
  ],
  "content_format": {
    "id": "5654ec02a6e02a37e70000d5",
    "name": "Standard Blog Post",
    "pitchable": true,
    "length_metric": "350-450 words",
    "quantity_options": [
      1
    ]
  }
}

Our writers can pitch you content ideas. A Pitchset is a lot like a Job (it’s forged from a JobTemplate with Prompt answers, Industries and Guidelines), but instead of being a request for writing, it is a request for topics to be pitched by our writers.

status can be any of the following:

Status Meaning
active Writers are generating topic pitches.
full Writers have finished pitching ideas.
marked_complete You archived the pitchset.
auto_closed You accepted as many pitches as you initially requested.
idle Writers are no longer pitching topics, because you have not responded to pitches.

Create a Pitchset

require 'scripted_client'

# First, find a JobTemplate that you'd like to use:

templates = ScriptedClient::JobTemplate.all
blog_post = templates.find { |template| template.name == 'Standard Blog Post' }

# Next, assign some values for the Prompts on that JobTemplate.

key_points = blog_post.prompts.find { |prompt| prompt.label == 'Key Points' }
key_points.value = ['Orangutans make great pets', 'Normal pets are lame']

# Next, you can find an Industry:

industries = ScriptedClient::Industry.all
lifestyle = industries.find { |industry| industry.name == 'Lifestyle & Travel' }

# Now you can create the Pitchset!

pitchset = ScriptedClient::Pitchset.new(
  tagline: 'Cool Ideas for Zoo-centric Content',
  number_of_jobs_requested: 3,
  job_template: blog_post,
  industries: [lifestyle]
)
pitchset.save
# => true
curl -H "Authorization: Bearer abcdefghij0123456789" \
  https://api.scripted.com/abcd1234/v1/pitchsets \
    -d tagline="Cool Ideas for Zoo-centric Content" \
    -d number_of_jobs_requested=3 \
    -d job_template[id]=5ceb8bb8235bcc76bf475e21 \
    -d job_template[prompts][][id]=52cdda2f8b588f7e02000062 \
    -d job_template[prompts][][value]=Yes \
    -d job_template[prompts][][id]=52cdda2f8b588f7e02000064 \
    -d job_template[prompts][][value]=#apifuntimes \
    -d guidelines[][id]=e9d378cc8e74b4b5faa34892935d5665 \
    -d guidelines[][id]=689ca0246221f2f21e80e9dac9387bce \
    -d industries[][id]=8af7c7ae67b6d0854051378b9f3eede4 \
    -d industries[][id]=92e4132ea0560de3c12f072dc9a81486 \
    -d industries[][id]=f0709aabc7050b3eed7c43db83f0f4cf

POST /:organization_key/v1/pitchsets

Creating a Pitchset is very similar to creating a Job. Pass a JobTemplate and answers for that JobTemplate’s Prompts. Either a list of Industries or a single Specialty must also be included. Guidelines are optional.

Parameter Description
job_template Required Pick a JobTemplate with a ContentFormat whose pitchable is true.
tagline Required Attract and engage writers with a catchy tagline.
number_of_jobs_requested Optional We’ll pitch you twice as many ideas as the number of jobs you request. Defaults to 1.
industries Required unless Specialty is passed A list of high-level categories pertaining to your job.
specialty Required unless Industries are passed A deeper field of knowledge.
guidelines Optional Tone, Voice and Perspective.

List all Pitchsets

ScriptedClient::Pitchset.all

# Or if you want to filter by state

ScriptedClient::Pitchset.requires_action
curl -H "Authorization: Bearer abcdefghij0123456789" \
    https://api.scripted.com/abcd1234/v1/pitchsets

# Or if you want to filter by state

curl -H "Authorization: Bearer abcdefghij0123456789" \
    https://api.scripted.com/abcd1234/v1/pitchsets/requires_action

GET /:organization_key/v1/pitchsets

You can pass any of the following filters to narrow your search:

Filter Meaning
open All Pitchsets in active, full, and idle states. The “Active” section of your “Topic Pitches” tab in the Scripted dashboard.
closed All Pitchsets in marked_complete and auto_closed states. The “Archived” section of your “Topic Pitches” tab in the Scripted dashboard.
requires_action Pitchsets in the open state with at least one pending Pitch. The “Action Items” tab in the Scripted dashboard.

Show a Pitchset

Don’t forget to replace 5ceb8bb8235bcc76bf475e21 with the ID of one of your pitchsets!

ScriptedClient::Pitchset.find('5ceb8bb8235bcc76bf475e21')
curl -H "Authorization: Bearer abcdefghij0123456789" \
    https://api.scripted.com/abcd1234/v1/pitchsets/5ceb8bb8235bcc76bf475e21

GET /:organization_key/v1/pitchsets/:id

Pitches

Sample Pitch

{
  "id": "5654ec24a6e02a37e7000772",
  "topic": "Ergonomic Cotton Pants",
  "body": "What are pants? You'll find out in this thrilling tale of rapid iteration in the garment industry. Wool to Cotton. Warm to Wearable. Economic to Ergonomic. I'll cover everything from the shoes to the belt and leave your readers craving new pants.",
  "status": "awaiting review",
  "created_at": "2015-11-24T23:00:52Z",
  "updated_at": "2016-01-07T17:09:05Z",
  "pitchset": {
    "id": "5654ec24a6e02a37e7000768",
    "tagline": "I'm a Traveling Pants Salesman. Pitch me Content Marketing ideas!"
  },
  "writer": {
    "id": "5654ec23a6e02a37e70006ef",
    "nickname": "Jeffrey Lebowski",
    "favorite": true
  }
}

Pitches are individual topic ideas pitched by Writers. You can accept or reject Pitches. If you accept a Pitch, it becomes a Job, to be completed by the Writer who pitched the idea.

Pitches are nested within Pitchsets, so all actions are namespaced under the URL of the Pitch’s Pitchset.

List all Pitches within a Pitchset

pitchset = ScriptedClient::Pitchset.all.first
pitchset.pitches
curl -H "Authorization: Bearer abcdefghij0123456789" \
    https://api.scripted.com/abcd1234/v1/pitchsets/5ceb8bb8235bcc76bf475e21/pitches

GET /:organization_key/v1/pitchsets/:pitchset_id/pitches

Accept a Pitch

pitchset = ScriptedClient::Pitchset.requires_action.first
pitch = pitchset.pitches.first
pitch.accept("This is some optional feedback to the Writer")
curl -H "Authorization: Bearer abcdefghij0123456789" \
    https://api.scripted.com/abcd1234/v1/pitchsets/5ceb8bb8235bcc76bf475e21/pitches/5def8bb8235bcc76bf345d5e/accept \
    -d feedback="You're just a straight shooter with upper management written all over you!"

POST /:organization_key/v1/pitchsets/:pitchset_id/pitches/:id/accept

You can pass feedback in the body of the POST request to accept a Pitch. Feedback will be passed along to the writer for guidance in writing the Job.

Reject a Pitch

pitchset = ScriptedClient::Pitchset.requires_action.first
pitch = pitchset.pitches.first
pitch.reject("This is some optional feedback to the Writer")
curl -H "Authorization: Bearer abcdefghij0123456789" \
    https://api.scripted.com/abcd1234/v1/pitchsets/5ceb8bb8235bcc76bf475e21/pitches/5def8bb8235bcc76bf345d5e/reject \
    -d feedback="Not quite what we were looking for. Try making it funnier."

POST /:organization_key/v1/pitchsets/:pitchset_id/pitches/:id/reject

You can pass feedback in the body of the POST request to reject a Pitch. Feedback will be passed along to the writer for guidance in coming up with future topic ideas.

Job Templates

ScriptedClient::JobTemplate.all
curl -H "Authorization: Bearer abcdefghij0123456789" \
    https://api.scripted.com/abcd1234/v1/job_templates

Sample JobTemplate

{
  "id": "5654ec02a6e02a37e70000cc",
  "name": "Standard Blog Post",
  "created_at": "2015-11-24T15:00:18-08:00",
  "content_format": {
    "id": "5654ec02a6e02a37e70000d5",
    "name": "Standard Blog Post",
    "pitchable": true,
    "length_metric": "350-450 words",
    "quantity_options": [
      1
    ]
  },
  "pricing": {
    "base": 9900,
    "specialist": 14900
  },
  "prompts": [
    {
      "id": "5654ec02a6e02a37e70000d8",
      "kind": "checkbox",
      "label": "Goal",
      "description": "Select one or many",
      "answer_required": false,
      "value_options": [
        "Informed analysis",
        "Thought leadership",
        "Repurpose existing writing",
        "Promote topic",
      ]
    },
    {
      "id": "5654ec02a6e02a37e70000da",
      "kind": "string[255]",
      "label": "Sample Blog",
      "description": "Link to an existing blog and describe why it's a good sample",
      "answer_required": false
    },
    {
      "id": "5654ec02a6e02a37e70000dc",
      "kind": "checkbox",
      "label": "Blog Structure",
      "description": "Select one or many",
      "answer_required": false,
      "value_options": [
        "Paragraphs",
        "Subheads",
        "Lists"
      ]
    },
    {
      "id": "5654ec02a6e02a37e70000de",
      "kind": "array",
      "label": "Key Points",
      "description": "List key points your writer should address",
      "answer_required": false
    },
    {
      "id": "5654ec02a6e02a37e70000e3",
      "kind": "radio",
      "label": "Links to Sources",
      "description": "Select one",
      "answer_required": false,
      "value_options": [
        "Include sources as linked anchor text",
        "Include sources as source list",
        "No sources"
      ]
    }
  ]
},

A JobTemplate has a ContentFormat, such as a Short Blog Post, and a collection of Prompts to that are designed to help guide your writer.

Content Formats

Sample ContentFormat

{
  "id": "5654ec02a6e02a37e70000d5",
  "name": "Standard Blog Post",
  "pitchable": true,
  "length_metric": "350-450 words",
  "quantity_options": [
    1
  ]
}

Embedded within each JobTemplate is a ContentFormat. By default, the ContentFormats we offer are: Standard Blog Post, Long Blog Post, Website Page, Article, Facebook Posts, Tweets, Press Release, Video Script, Product Descriptions.

Prompts

Sample Prompt

{
  "id": "5654ec02a6e02a37e70000d8",
  "kind": "checkbox",
  "label": "Goal",
  "description": "Select one or many",
  "answer_required": false,
  "value_options": [
    "Informed analysis",
    "Thought leadership",
    "Repurpose existing writing",
    "Promote topic",
  ]
}

Prompts are question/answer pairs that help guide your writer. They can be one of five kinds: string[255] string[1024] radio checkbox array. The data type of the value that you post will depend on the kind of the Prompt:

Kind Value Type Has value_options?
string[255] String (max. 255 characters) No
string[1024] String (max. 1024 characters) No
radio String Yes
checkbox Array Yes
array Array No

Industries

ScriptedClient::Industry.all
curl -H "Authorization: Bearer abcdefghij0123456789" \
    https://api.scripted.com/abcd1234/v1/industries

Sample Industry

{
  "id": "5654ebfc93be3b2c623b6e2f",
  "name": "Publishing & Journalism",
  "specialties": [
    {
      "id": "5654ebfc93be3b2c623b6eab",
      "name": "Self Publishing"
    }
  ]
}

GET /:organization_key/v1/industries

Industries specify the topic area of your Job or Pitchset. They also have a list of associated Specialties if you’d like a Writer with more depth of knowledge.

Specialties

ScriptedClient::Specialty.all
curl -H "Authorization: Bearer abcdefghij0123456789" \
    https://api.scripted.com/abcd1234/v1/specialties

Sample Specialty

{
  "id": "5654ebfc93be3b2c623b6eb1",
  "name": "Video Games"
}

GET /:organization_key/v1/specialties

If you’re willing to pay a bit more (see JobTemplate#pricing) for a Specialist writer, you can include a Specialty (instead of a list of Industries) when you create a Job or Pitchset.

Guidelines

ScriptedClient::Guideline.all
curl -H "Authorization: Bearer abcdefghij0123456789" \
    https://api.scripted.com/abcd1234/v1/guidelines

Sample Guideline

{
  "id": "5654ebfc93be3b2c623b6e63",
  "name": "Anecdotal",
  "kind": "Tone"
}

GET /:organization_key/v1/guidelines

Guidelines are optional. Jobs and Pitchsets can have many of them.

Pagination

jobs = ScriptedClient::Job.all
jobs.has_next?
# => true
page_two = jobs.next
curl -H "Authorization: Bearer abcdefghij0123456789" \
    https://api.scripted.com/abcd1234/v1/jobs \
    -d next_cursor='MTQwMDg4OTU0NDoy'

Sample Cursor

{
  "data": [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
  "paging": {
    "has_next": true,
    "next_cursor": "MTQwMDg4OTU0NDoy"
  }
}

We use cursor-based pagination. A single API request returns 15 records by default. If a list is longer than 15 objects, we’ll return the first page and a next_cursor that you can pass on your next request to the list endpoint.