Store Integration
The Store MCP gives you the full management surface for your listing — register your app, set pricing, publish, and run promos. This page lists each tool, then shows how your app verifies a student has paid for access at launch.
Tools
All tools require Store-admin access for the targeted app.
| Tool | What it does |
|---|---|
register_app |
Register an already-approved Platform app in the Store catalog. You provide a Store-side slug, a reference to the Platform app (ref), optional plan (priceCents, currency, additionalSeatCents, maxSeats, paymentModel — one of one_time / subscription_monthly / subscription_annual, defaults to one_time), listing metadata (including an optional listing.buyerLtiLaunchEnabled boolean that toggles the Post-Purchase Parent LTI Launch), an optional defaultOrgSourcedId — when set, the platform-side org is verified up front and every entitled student gets a student/primary role in that org on purchase (in addition to the standard user_profile writeback), and an optional webhookUrl — required only if your app will call the in-app purchases endpoint. Returns Cognito M2M clientId/clientSecret for the REST surface and, when webhookUrl is set, a one-shot outboundWebhookSigningSecret for verifying signed webhook deliveries — capture both, they aren't retrievable via subsequent MCP calls. Creates the catalog entry in an unpublished state — call set_app_published once the listing is ready. |
update_app_plan |
Update the plan attached to a Store app: base priceCents, currency, optional additionalSeatCents (per-seat pricing beyond the included seats), optional maxSeats, and optional paymentModel (one_time / subscription_monthly / subscription_annual). Existing values are preserved when fields are omitted, so you can change just one dimension at a time. |
create_catalog_item |
Create a catalog item on a store app's plan: a displayName, priceCents, and ISO 4217 currency. In-app purchases reference these items by id. You supply the planId the item attaches to; grants are checked against the app that owns that plan. |
manage_app_image |
Set the logo, set the cover, add a gallery screenshot, or remove a gallery screenshot. Pass action plus slug and either imageBase64 (base64 bytes, ≤5 MB after encoding) or imageUrl (HTTPS, SSRF-guarded, ≤5 MB, image/* content-type) for the three set/add actions; remove_screenshot takes contentHash instead. Idempotent by content hash within the same kind; the same bytes cannot be reused across different kinds on one app. |
set_app_published |
Flip a Store app's published flag. Setting it to true makes the listing visible on store.timeback.com; false unpublishes it without deleting catalog state. Idempotent. |
create_discount_code |
Create a discount code redeemable at checkout. Choose flat (cents off) or percent (basis points off), set an optional expiresAt, an optional redemption cap, and an optional app scope so the code only works for specific listings. |
list_discount_codes |
List the discount codes you've created, optionally filtered by storeAppId or discount type. Returns the codes you can act on. |
create_affiliate |
Create an affiliate with a unique referral code, a payoutShareBp (basis points of net sale paid out), and an optional app scope so the affiliate only earns on specific listings. |
update_affiliate |
Update an existing affiliate's state (active/inactive), payoutShareBp, display name, contact email, or app scope. Idempotent. |
refund_purchase |
Refund a storefront or in-app purchase your app owns (full or partial). Pass purchaseType (storefront or in-app), the purchaseId, the storeAppId that owns it, and — for a partial — amountCents (strictly less than the purchase amount). Enforces the 7-day refund window and the one-refund-per-purchase rule. See Store → Refunds for entitlement side effects and outbound webhook payloads. |
The tools' full input schemas are served by the MCP server itself — your client fetches them on connection, so the field names and types stay in sync with whatever the server is currently running.
In-App Purchases
Once your app is registered and you've created a catalog item, your backend can charge the parent's saved payment method off-session via POST /apps/v1/in-app-purchases and receive the outcome on the webhookUrl you registered. See Store → In-App Purchases for the full walkthrough — M2M token flow, request/response, and outbound webhook verification.
Refunds
Both storefront purchases and in-app purchases can be refunded (full or partial) via POST /apps/v1/{purchases|in-app-purchases}/{id}/refund or the refund_purchase MCP tool. Storefront full refunds revoke the student's entitlement and cancel the underlying Stripe subscription (matching the parent self-serve full refund); partial and IAP refunds leave the entitlement active. Every refund fires a signed outbound webhook (purchase.refunded or in-app-purchase.refunded) to the webhookUrl you registered. See Store → Refunds for the full flow, the 7-day window, the one-refund-per-purchase rule, and the webhook envelopes.
Post-Purchase Parent LTI Launch
When enabled on your listing, the Store LTI-launches the buyer into your app right from the checkout success page — no manual sign-in, no separate handoff. Off by default; you opt in per app.
Enable it
Pass listing.buyerLtiLaunchEnabled: true on register_app (the tool upserts, so you can flip it on an already-registered app by re-invoking with just this field on listing).
What the parent sees
Under the fulfillment section of store.timeback.com/checkout/success, a cancellable countdown ("Opening your app name in N seconds…" + a Cancel button) plus a persistent Take Me to your app name button. Both open a new tab and POST an LTI 1.3 id_token to your registered launchUrl — the same launch mechanics documented in Level 2: LTI Launch.
What your app receives
The Store appends the just-purchased students' OneRoster sourcedIds to the LTI target_link_uri claim as ?purchasedStudents=<sourcedId> (comma-separated for multi-student checkouts). After your LTI endpoint validates the token and redirects the browser to target_link_uri, your landing page can read the query param and know exactly which kids just came out of checkout:
GET https://your-app.example.com/lti/landing?purchasedStudents=abc-...,def-...
For example, an Express handler:
app.get('/lti/landing', (req, res) => {
const purchasedStudentSourcedIds = String(req.query.purchasedStudents ?? '')
.split(',')
.filter(Boolean);
// greet the newly purchased students, kick off onboarding, unlock content, etc.
});
The query param is present only on launches originating from a completed Store checkout; other LTI launches into your app (e.g., a returning student from TBA) don't carry it. Treat it as an optional hint, not an authorization signal — always rely on the LTI id_token claims to authenticate the caller.
Check Student Access at Login
When a student launches your app, you need to confirm they have a paid, active profile for it before letting them in. This runs against the Platform's OneRoster API (not the Store) and takes two short requests.
Step 1: Mint a Platform access token
Use your clientId + clientSecret from app registration to mint a Bearer token. See Authentication for the full client-credentials flow:
curl -X POST https://platform.timeback.com/auth/1.0/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-u "<CLIENT_ID>:<CLIENT_SECRET>" \
-d "grant_type=client_credentials&scope=https://purl.imsglobal.org/spec/or/v1p2/scope/roster.readonly"
Cache the returned access_token — it's valid for an hour.
Step 2: Resolve the student's sourcedId by email
GET /rostering/1.0/users returns one row when filtered by email. Trim the response to just the sourcedId:
curl "https://platform.timeback.com/rostering/1.0/users?filter=email='student@example.com'&fields=sourcedId" \
-H "Authorization: Bearer <ACCESS_TOKEN>"
{
"users": [{ "sourcedId": "11111111-2222-3333-4444-555555555555" }],
"offset": 0,
"limit": 10,
"total": 1
}
If total is 0, the email isn't on TimeBack — deny access.
Step 3: Assert an active profile for your app
GET /rostering/1.0/users/{sourcedId}/profiles returns the user's profiles; filter by your applicationId and ask only for the fields you'll inspect:
curl "https://platform.timeback.com/rostering/1.0/users/11111111-2222-3333-4444-555555555555/profiles?applicationId=<YOUR_APP_ID>&userId=11111111-2222-3333-4444-555555555555&fields=profileType,status" \
-H "Authorization: Bearer <ACCESS_TOKEN>"
{
"userProfiles": [{ "profileType": "learning_app_profile", "status": "active" }],
"offset": 0,
"limit": 10,
"total": 1
}
Treat the launch as authorized only when a profile exists and its status is active. An empty userProfiles array, a 404, or any status other than active means the student hasn't paid for your app (or their purchase was refunded) — deny the launch.
The same two requests run unchanged against sandbox.platform.timeback.com with sandbox credentials — useful when you're testing the entitlement gate before publishing.
Related Docs
Store → Introduction
What the Store provides, the publish path, and the prerequisites.
Store → MCP Setup
Connect Cursor / VS Code / Claude Code / ChatGPT to the Store MCP.
Store → In-App Purchases
Server-to-server purchase endpoint, M2M authentication, and outbound-webhook verification.
Store → Refunds
Full and partial refund flows, the 7-day window, entitlement side effects, and the refund_purchase MCP tool.
Authentication
The client-credentials flow used to mint the Bearer token for the entitlement check above.
Level 0: Register Your App
How to get the applicationId, clientId, and clientSecret referenced throughout this page.
