Developers
One prompt, one tag, or no code at all.
claude mcp add --transport http tenure \
"https://app.trytenure.co/api/mcp?profile=install" \
--header "Authorization: Bearer $TENURE_TEST_KEY"Needs a free Tenure account: create a test key in Settings › Developer after sign-up.
Agent install
Agents draft. People publish.
- 01Create a test keyThe Read, Runtime and Draft scopes, a 7-day expiry. It lives in the agent's environment only.
- 02Paste the promptFrom Settings › Install, into Claude Code, Cursor or any coding agent.
- 03The agent proposesFacts to track, where to mount cards, how users map to billing, each with a citation.
- 04You reviewAccept or decline each item. Publishing stays with a signed-in owner or admin.
$ Connected to tenure over MCP, with a test key ✓ get_app_definition · get_verification✓ propose_facts · 7 facts, each with file:line✓ widget wired · simulate_decision→ submit_for_review. A person decides.
Install
Example checks; your setup determines which are required.- Billing is talking to Tenure
- We know who your users are
- Your app is telling us what happens
- Customers can see it
- Optional: test billing change
- Email sending is verified
Or let your AI coding agent do it
A prompt for Claude Code, Cursor or Copilot. The agent proposes and tests; you review and publish.
First create a test key and put it in the agent’s environment as TENURE_TEST_KEY. Then paste the prompt in your repository. The agent reads your code, proposes the signals your app should send, wires the widget, runs a test decision and sends you its proposals to review. It cannot publish.
Install Tenure in this app. Tenure runs the cancel flow, win-back and in-app offers for this subscription product. You prepare the install; a person reviews it and publishes. Never ask for, or attempt, publishing, confirming or enabling anything.
- 1.Read https://app.trytenure.co/llms.txt first, then the pages it lists under "Install in one sitting" (install.md, mcp.md, events.md).
- 2.Connect to Tenure's MCP server: POST https://app.trytenure.co/api/mcp?profile=install with the header Authorization: Bearer $TENURE_TEST_KEY. That is a test key (it starts ck_test_) with the Read, Runtime and Draft scopes, expiring in 7 days. It can read, send test events, propose and submit for review; it cannot publish.
- 3.Call ping, get_app_definition and get_verification: what Tenure already knows, and which checks your setup needs, what is missing and what each would unlock.
- 4.Read this codebase to find how a signed-in user maps to a billing customer, the product's meaningful actions, and where the billing page, the cancel button and any locked features live.
Ready means your required checks pass, each read from real evidence. Required checks depend on your setup and experiences; test billing changes are optional diagnostics, and simulations and previews never count as production evidence.
MCP server
One endpoint, scoped keys.
Streamable HTTP. Keys carry the Read, Runtime and Draft scopes (read, decide and draft_write in the API); there is no publish scope.
- Endpoint
https://app.trytenure.co/api/mcp- Key
- A test key (ck_test_…). Draft writes are capped at 120 an hour per key.
Inspect
pingget_app_definitionget_verificationget_review_status
Test
track_eventsimulate_decisionreport_slots
Propose
propose_factspropose_slotspropose_identity_mappingsubmit_for_review
Decide
recommend_retention_offerreport_offer_eventenable_retention_offerescalate_to_human
Widget
One tag, on the page you have.
The flow loads only when someone reaches for cancel.
- About 5 KB · gzipped, on every page
- Shadow DOM · its own, so your styles stay yours
- Fails open · Tenure can’t load? Your cancel link runs
<script src="https://app.trytenure.co/widget.js" data-key="pk_..." data-customer="USER_ID"
data-auth="SIGNED_HASH" data-mode="test"></script>
<!-- Mark the cancel button you already have. Its href is followed
after the subscriber confirms cancelling. -->
<a href="/account/cancel/confirm" data-tenure-cancel>Cancel subscription</a>data-auth is the lowercase hex HMAC-SHA256 of the subscriber id, keyed with your widget signing secret (Settings › Install), computed on your server. Remove data-mode when you switch to live.
REST and webhooks
Your own screens, the same rules.
Recommend, report what was shown, then enable. Cancel always exits.
const rec = await tenure.recommend({ customerId: user.id, reason: "too_expensive",
conversation: "It's too expensive for us now" });
if (rec.decision === "offer" && rec.offer) {
await tenure.deliveries.report(rec.deliveryId!, "rendered");
await tenure.enable({ sessionId: rec.sessionId!, action: "accept" }); // on an explicit yes
} else if (rec.sessionId) {
await tenure.enable({ sessionId: rec.sessionId, action: "cancel" }); // cancel always exits
}SDK-style example for teams with the source package. It is not published on npm; use the package-free REST examples in the docs. The decision runs your published Cancellations plan.
// Node 18+, no SDK package required. Pass the ORIGINAL raw body.
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyTenureWebhook(rawBody, signature, timestamp, secret) {
if (!secret || typeof signature !== "string" ||
!/^[a-f0-9]{64}$/i.test(signature) ||
typeof timestamp !== "string" || !/^\d+$/.test(timestamp)) return null;
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return null;
const expected = createHmac("sha256", secret)
.update(timestamp + ".").update(rawBody).digest();
const received = Buffer.from(signature, "hex");
if (!timingSafeEqual(expected, received)) return null;
try {
const event = JSON.parse(rawBody.toString());
return event && typeof event.event === "string" ? event : null;
} catch { return null; }
}
// In your handler, pass x-tenure-signature and x-tenure-timestamp
// headers plus your webhook signing secret. Reject a null result.
// Handle each eventId once: retries reuse it. Do not parse or
// re-serialize the body before verification.Verify the original raw body with your signing secret, then deduplicate by eventId. HMAC-SHA256; retries reuse the event ID.
Start with a test key.
New workspaces start in test mode: test keys see test data only, and nothing reaches a real customer until you publish.