Skip to contentDevelopersPrezly website
Contacts and outreach

Individual pitches

Prepare a one-to-one pitch for a known contact, review its message, and submit it without blind retries.

A pitch is a one-to-one email to a selected CRM contact and address. Use it for a message you review for that person. Use an email campaign when one reviewed message should go to a selected audience.

Creating a pitch saves a draft. Sending it is a separate action that delivers real email. Keep the final send outside any loop that creates or edits drafts.

Prepare the contact, sender, and access

Load your credential into PREZLY_API_TOKEN. Personal tokens need pitches:read, pitches:write, and pitches:send for the respective operations. Reading the contact also needs contacts:read; reading an attached story needs stories:read. The owner needs manage_emails for pitch access and send_emails plus available sending allowance to submit it.

Read the CRM contact and choose its numeric ID and intended address. The address must belong to the contact; a pitch is not a way to send to an unrelated raw address. If recipient.email_address is omitted, the primary contact address is used. Having the contact in your directory does not establish subscription consent.

Choose an approved numeric sender ID. List Sender Addresses requires an integration key; with a personal token, use an already known sender configured in Prezly. The pitch field is sender_address, unlike a campaign's sender.

Find existing pitches before creating another

List Pitches returns pitches, pagination, and sort; Search Pitches accepts JSON-body search parameters. Persist the numeric pitch.id for each integration task. Use pagination to inspect a complete selection and the existing ID to resume work after an interrupted request.

Do not identify a pitch solely by its subject: several messages can intentionally have the same subject. If Create times out, reconcile existing work before creating another draft.

Prepare a draft payload

This Node.js 22+ script writes pitch-draft.json. Replace the illustrative numeric contact/sender/story IDs and selected address. Save it as prepare-pitch.mjs, review the message, and run node prepare-pitch.mjs:

import { writeFile } from 'node:fs/promises';

const document = {
  type: 'document',
  version: '2.5',
  children: [{
    type: 'paragraph',
    children: [{ text: 'Hi Alex, I thought our new studio announcement might interest you. Would you like more information?' }],
  }],
};
const payload = {
  sender_address: 34567,
  recipient: { contact: { id: 12345 }, email_address: 'alex@example.test' },
  subject: 'Acme studio announcement for your architecture coverage',
  content: JSON.stringify(document),
  story: { id: 23456 },
  is_open_tracking_enabled: false,
  is_click_tracking_enabled: false,
};
await writeFile('pitch-draft.json', JSON.stringify(payload, null, 2));
console.log('Wrote a draft payload. No pitch was created or sent.');

content is a serialized email document string inside the JSON request, not raw HTML or a nested document object. This minimal paragraph uses the supported 2.5 email format. The story format guide describes story bodies; do not copy a complete story document into an email unchanged. The attached story is separate, and its newsroom is aligned by the API. Review its visibility and availability for the intended recipient; omit story if the message should have no attached story.

The script deliberately chooses tracking settings. Review those choices for your outreach instead of inheriting them accidentally.

curl --fail-with-body --silent --show-error --request POST 'https://api.prezly.com/v2/pitches' \
  --header "Authorization: Bearer $PREZLY_API_TOKEN" \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data-binary @pitch-draft.json

Successful creation returns 201 Created with pitch and warnings. Save pitch.id. Drafts may be incomplete; successful creation does not mean the message can be sent.

Create Pitch →

Read, review, and edit the draft

Replace YOUR_PITCH_ID with the saved numeric ID:

curl --fail-with-body --silent --show-error 'https://api.prezly.com/v2/pitches/YOUR_PITCH_ID' \
  --header "Authorization: Bearer $PREZLY_API_TOKEN" \
  --header 'Accept: application/json'

The response contains pitch and warnings. Confirm the ID, status: draft, sender, recipient contact/address, subject, content, story, tracking settings, and warnings. The returned email body is pitch.email_content; the write field is content.

Use Update Pitch for a partial draft update, for example:

curl --fail-with-body --silent --show-error --request PATCH 'https://api.prezly.com/v2/pitches/YOUR_PITCH_ID' \
  --header "Authorization: Bearer $PREZLY_API_TOKEN" \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{"subject":"Acme studio announcement — details for Alex"}'

Read the returned pitch and warnings again. Coordinate editors: a read is not a lock or an atomic expected-state check. Keep changes in draft; do not rely on editing an immediate send already in flight. For an existing scheduled pitch, use Prezly to unschedule it before editing.

The public pitch contract covers draft preparation and immediate send. It does not support scheduling or changing CC/BCC recipients. Existing pitches may expose scheduled_at, copies, or follow-up fields; those response fields are not a promise that the same fields are writable. Follow-up recipient, subject, sender, and site constraints can also prevent changes; create a separate new message when the intended conversation changes.

Send once and reconcile the result

Before sending, check the selected person/address, message, sender, attached story/link, tracking, and current warnings in Prezly. Unsubscription warnings block sending. Bounced-address warnings are also worth reviewing: the pitch implementation can accept a bounced recipient, so API acceptance is not a guarantee of deliverability.

After the final review, submit once:

curl --fail-with-body --silent --show-error --request POST 'https://api.prezly.com/v2/pitches/YOUR_PITCH_ID/send' \
  --header "Authorization: Bearer $PREZLY_API_TOKEN" \
  --header 'Accept: application/json'

A 202 Accepted response contains pitch. Unlike Campaign Send, it does not return a job reference. It means the pitch was accepted for asynchronous sending, not that the recipient has received it.

Re-read Get Pitch and inspect status, is_processing, is_sent, delivery timestamps, and workflow_statuses. final means a finalized/queued state; it is not proof of delivery. sent_at alone is not an inbox receipt either.

If Send times out or its response is lost, retrieve the same pitch and reconcile its current state. Do not automatically repeat Send or create a replacement pitch. The public contract does not provide an idempotency key or the MCP tools' expected_* fields for this operation.

  • 403: check personal-token endpoint eligibility, the owner's permissions, and available sending allowance.
  • 405: a pitch may already have been sent; read its state rather than attempting to edit it again.
  • 409: inspect missing content, recipient, sender, subscription, and other blocking conditions.
  • 422: inspect request field names, types, and referenced records.

See Handling errors for response parsing and retry decisions.

Send Pitch →

On this page