# Create a draft

### Create a draft

`POST /api/v1/drafts`

Operation ID: `createDraft`

Draft a message, InMail or connection request for a lead. Nothing is sent until a member approves it in the Needle inbox, where they can also edit or discard it. A conversation (account and lead) has one draft waiting at a time: creating another replaces a waiting draft created through the API, which is discarded as `superseded`. A draft a campaign step holds is never replaced; creating one then fails until it is reviewed.

**Request body** (required)

- `connectedAccountId` (string · uuid, required): The account that sends it. Its channel must match `action`.
- `leadId` (string · uuid, required): The lead it goes to.
- `action` ("linkedin_message" | "linkedin_inmail" | "linkedin_invitation" | "instagram_message", required): `linkedin_invitation` sends a connection request with `text` as its note.
- `text` (string, required): Sent exactly as written, so {{variables}} are not allowed. Up to 300 characters for a connection note and 1900 for an InMail.
- `subject` (string, optional): Required for `linkedin_inmail`, not allowed otherwise.

**201**: Draft created and waiting for review.

- `draft` (object, required)
  - `id` (string · uuid, required)
  - `status` ("awaiting_review" | "sent" | "failed" | "discarded", required): `awaiting_review` until a member approves or discards it in the Needle inbox. Approving sends it right away: it becomes `sent`, or stays `awaiting_review` with `lastError` when the send fails. `failed` means it could not be sent and cannot be approved again; `discarded` means it will not be sent.
  - `channel` ("linkedin" | "instagram", required)
  - `action` ("linkedin_message" | "linkedin_inmail" | "linkedin_invitation" | "instagram_message", required)
  - `text` (string, required): What will be sent, or was sent. A member may edit it before approving.
  - `subject` (string | null, required): The InMail subject.
  - `lead` (object, required)
    - `id` (string · uuid, required)
    - `name` (string | null, required)
  - `connectedAccountId` (string · uuid, required)
  - `campaignId` (string · uuid | null, required): Set when a campaign step drafted the message.
  - `taskId` (string · uuid | null, required): The campaign task the draft holds. Null for API drafts.
  - `discardReason` ("rejected" | "superseded" | "withdrawn" | "enrollment_stopped" | "campaign_archived" | "task_cancelled" | null, required)
  - `lastError` (string | null, required): Why the last attempt to send it failed.
  - `createdBy` (string · uuid | null, required): The member who created it. Null when a campaign step did.
  - `reviewedBy` (string · uuid | null, required): The member who approved or discarded it.
  - `reviewedAt` (string · date-time | null, required)
  - `createdAt` (string · date-time, required)
  - `updatedAt` (string · date-time, required)
- `requestId` (string · uuid, required): Id of this request, for support and troubleshooting

**Errors**

- `400`: Bad request
- `401`: Unauthorized: missing or invalid API key
- `403`: Forbidden
- `404`: Not found
- `409`: The lead has no profile on the account’s channel, is on the do-not-contact list, or a campaign step already has a draft waiting in this conversation.
- `422`: Invalid request payload
