Drafts & approvals
Every email Trimline sends starts as a draft. Review, edit, approve, hold or discard them.
A draft is an email we’ve written and that is waiting for a decision. Every message Trimline sends to a vendor starts as a draft. Unless you’ve turned on autopilot for that vendor, a draft is only sent once someone approves it, in the dashboard, from Slack, or through this API.
Endpoints
- GET/v1/draftsList drafts
- GET/v1/drafts/:idRetrieve a draft
- PATCH/v1/drafts/:idEdit before sending
- POST/v1/drafts/:id/approveApprove and send
- POST/v1/drafts/:id/holdHold
- POST/v1/drafts/:id/discardDiscard
The draft object
{
"id": "drf_3Nc81Ab",
"object": "draft",
"negotiation": "neg_8Hk2Pq",
"renewal": "ren_1Pq8sT2v",
"kind": "counter_offer",
"status": "pending",
"from": "sam@northwind.example",
"to": ["priya@acmecrm.example"],
"cc": [],
"subject": "Re: Acme CRM renewal — Northwind Labs",
"body": "Thanks Priya, that's helpful, and flat pricing for both years works for us. To sign this month we'd need the discount at 22%…",
"rationale": "Vendor opened at 12%. Benchmarks for 25–50 seat accounts show 18–25% on 2-year terms. Countering at 22% leaves room to settle near 18%.",
"expires_at": "2026-12-05T16:42:10Z",
"approved_by": null,
"sent_at": null,
"created_at": "2026-12-03T16:44:02Z",
"livemode": false
}
kindenum | opening, follow_up, counter_offer, acceptance, notice or question. |
statusenum | pending, approved (queued to send), sent, held, discarded or expired. Once a draft is sent or discarded, its subject and body are erased from Trimline. The sent copy lives only in your mailbox. |
rationalestring | Why we wrote what we wrote, in plain language. It’s never sent to the vendor. |
expires_attimestamp, nullable | After this time, the draft is stale, usually because the vendor’s offer has a deadline. Expired drafts are replaced with a fresh one. |
List drafts
The most common call is status=pending, which returns everything waiting on your team.
curl -G https://api.trimline.tech/v1/drafts \
-H "Authorization: Bearer $TRIMLINE_API_KEY" \
-d "status=pending"
Edit a draft
Change the subject, body, to or cc before approving. Edited drafts are marked edited: true, and we learn from your edits for future messages to this vendor.
Approve a draft
Queues the draft to send from the connected mailbox in from. Requires the drafts:write scope and an idempotency key.
approved_byrequiredstring | The email address of the person approving. It’s recorded in the audit log. |
send_attimestamp | Schedule the send for later, for example during the recipient’s business hours. |
curl -X POST https://api.trimline.tech/v1/drafts/drf_3Nc81Ab/approve \
-H "Authorization: Bearer $TRIMLINE_API_KEY" \
-H "Idempotency-Key: approve-drf_3Nc81Ab" \
-H "Content-Type: application/json" \
-d '{ "approved_by": "sam@northwind.example" }'
// e.g. from a Slack button handler
export async function approveDraft(draftId, approver) {
const res = await fetch(`https://api.trimline.tech/v1/drafts/${draftId}/approve`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TRIMLINE_API_KEY}`,
"Idempotency-Key": `approve-${draftId}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ approved_by: approver }),
});
if (res.status === 409) return "already handled";
if (!res.ok) throw new Error((await res.json()).error.message);
return "sent";
}
def approve_draft(draft_id: str, approver: str) -> str:
res = requests.post(
f"https://api.trimline.tech/v1/drafts/{draft_id}/approve",
headers={**HEADERS, "Idempotency-Key": f"approve-{draft_id}"},
json={"approved_by": approver},
)
if res.status_code == 409:
return "already handled"
res.raise_for_status()
return "sent"
Returns 409 draft_not_pending if the draft has already been dealt with, which is safe to ignore when you have several approval surfaces.
Hold or discard
POST /v1/drafts/:id/hold parks the draft without sending. The negotiation pauses until you approve it or ask for a new version. POST /v1/drafts/:id/discard throws it away. Pass "regenerate": true with optional instructions, such as "be warmer, mention we've been customers for 3 years", to get a replacement.
Something unclear or missing? Email developers@trimline.tech.