Trimline is in private beta. Public launch: June 2027.Join the waitlist →
Docs / Resources

Renewals

One upcoming decision: a contract that's about to renew, and what you'll do about it.

Beta. The Trimline API is available to private beta customers. Endpoints may change before general availability in June 2027, and every breaking change is announced in the changelog. Request access.

A renewal represents one upcoming decision: a contract that’s about to renew, and what you’re going to do about it. Trimline opens a renewal 120 days before the contract’s term_end, or as soon as we find a contract that’s already inside that window.

Endpoints

The renewal object

{
  "id": "ren_1Pq8sT2v",
  "object": "renewal",
  "vendor": { "id": "ven_9xKd02", "name": "Acme CRM" },
  "contract": "ctr_4Lw7Yh",
  "renews_on": "2027-02-01",
  "notice_deadline": "2026-12-02",
  "days_until_deadline": 23,
  "status": "negotiating",
  "current_annual_value": { "amount": 3840000, "currency": "usd" },
  "proposed_annual_value": { "amount": 4108800, "currency": "usd" },
  "estimated_savings": {
    "low": { "amount": 384000, "currency": "usd" },
    "high": { "amount": 960000, "currency": "usd" }
  },
  "best_offer": "off_6Tg2Ws",
  "negotiation": "neg_8Hk2Pq",
  "outcome": null,
  "livemode": false
}

Statuses

StatusMeaning
upcomingOpened, no decision yet.
negotiatingA negotiation is in progress.
awaiting_decisionAn offer is on the table and needs your yes or no.
acceptedYou accepted new terms, and the order form is being signed.
renewedThe contract renewed. outcome records on what terms.
cancelledNotice was given and the contract will end.
skippedYou chose to renew as-is without negotiating.

New statuses may be added. Treat unknown values as upcoming.

Other fields

proposed_annual_valuemoney, nullableWhat the vendor plans to charge if nothing changes, including any uplift.
estimated_savingsobject, nullableOur estimated savings range, based on benchmarks for this vendor and your usage. It’s an estimate, not a promise.
outcomeobject, nullableSet once the renewal closes: the final annual value, term and a link to the savings record.

List renewals

GET/v1/renewals

Sorted by notice_deadline, soonest first.

statusenum or listFor example status=upcoming or status[]=upcoming&status[]=negotiating.
notice_deadline[lte]dateDeadlines on or before this date.
vendorstringRenewals for one vendor.

Retrieve a renewal

GET/v1/renewals/:id

Supports expand[]=negotiation and expand[]=contract.

Start negotiating

POST/v1/renewals/:id/negotiate

Opens a negotiation and generates the first draft to the vendor. Nothing is sent until the draft is approved.

target_annual_valuemoneyThe price you’d be happy with. If you leave it out, we use the low end of estimated_savings.
leversarray of enumAny of price, seats, uplift, term, plan and payment_terms. The default is all of them.
max_term_monthsintegerThe longest commitment you’ll accept. The default is 24.
toneenumfriendly, neutral (the default) or firm.
curl -X POST https://api.trimline.tech/v1/renewals/ren_1Pq8sT2v/negotiate \
  -H "Authorization: Bearer $TRIMLINE_API_KEY" \
  -H "Idempotency-Key: negotiate-ren_1Pq8sT2v" \
  -H "Content-Type: application/json" \
  -d '{
    "target_annual_value": { "amount": 3000000, "currency": "usd" },
    "levers": ["seats", "uplift", "term"],
    "max_term_months": 24
  }'
await fetch("https://api.trimline.tech/v1/renewals/ren_1Pq8sT2v/negotiate", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.TRIMLINE_API_KEY}`,
    "Idempotency-Key": "negotiate-ren_1Pq8sT2v",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    target_annual_value: { amount: 3000000, currency: "usd" },
    levers: ["seats", "uplift", "term"],
    max_term_months: 24,
  }),
});

Skip a renewal

POST/v1/renewals/:id/skip

Records that you’ve decided to renew on the current terms. We won’t contact the vendor, and no fee applies. Pass an optional reason string for your own records.

Something unclear or missing? Email developers@trimline.tech.