← AWS Offer Desk
API
Tokens

Run AWS Offer Desk from your own code

Both metered lanes are available over HTTP and each returns one JSON object. The division of labour: the page reads the listing setup and the private offer draft, decides the fit verdict, recommends the pricing model, builds the irreversible-choice register, runs every listing check and every offer check, computes the fee on the pre-tax total contract value, settles the buyer pre-check, places this quarter's co-sell rung and lays out the timeline to the first deal, then hands it all to the run as facts. The run plans the listing or reviews the offer; the page then holds it to its own facts (awreply.js). AWS Offer Desk is about AWS Marketplace SaaS listings and their private offers - not job offers or sales quotes - and is not affiliated with or endorsed by Amazon Web Services.

Base URL and the response shape

Every endpoint lives under https://api.skillsafe.ai/v1/app-api, and every response has one shape:

{ "ok": true,  "data":  { ... } }
{ "ok": false, "error": { "code": "...", "message": "..." } }

The token is minted for this app (the guest call takes {"slug":"aws-offer-desk"}), so no slug header is needed afterwards. Send it as Authorization: Bearer YOUR_TOKEN. The run body is the input object, sent bare.

StatusCodeMeaning
400validation_errorThe body is not a JSON object, or a required key (task, listing, dimensions, facts) is missing. The page always sends all six of task, listing, dimensions, offer, carry, facts; carry is an empty string for plan, and offer is an empty string when no offer is drafted.
401unauthorizedNo token, or a stale one. Mint a guest token or sign in again.
402insufficient_creditsThe balance is under min_credits. Price with /estimate first.
403forbiddenA guest token tried to run: both lanes are metered and need a personal sign-in.
404not_foundUnknown job id.
429rate_limitedBack off and retry.
5xxserver_errorTransient. Retry with the same Idempotency-Key so a retry never double-bills.

The task field comes first

Two lanes, chosen by task, in the order the work runs - the listing, then each private offer:

A missing or unknown task is answered as plan. Both lanes send the same six fields. The facts are the page's checks, one row per line, and a line starting "must:" is a rule the reply has to hold. The page's handoff carries the plan's model, fit, register and fixes into carry for the offer lane.

The input

FieldRequiredWhat
taskyesplan or offer. If it is missing or neither, do plan and set "task": "plan".
listingyesthe listing setup as the page read it: the product, the pricing model, the stage, another public paid product, the public terms and the trial, the seller account, the hosting, a third-party service off AWS, the badge, Partner Central, the buyers, the offer owner, the engineering weeks, the target date with its days from today, the countries, how the vendor sells today and the public price changes planned.
dimensionsyesone line per dimension: API name, display name, kind, unit and public price.
offersend it (empty with no offer drafted; offer needs it)the private offer draft as the page read it (buyer, country, renewal, renewal flag, replacement, total contract value, term, net terms, auto-renewal, expiry, CPPO and the wholesale price, professional services, the installments with their index and date, the buyer-side notes), or empty when no offer is drafted.
carrysend it (empty for plan)offer task only: the listing plan's model, fit, register and fixes when the plan ran first; may be empty.
factsyesthe page's listing checks, one line each with the rule, the id, the subject, the status and the evidence separated by vertical bars; then the fit verdict, the pricing model, the register, the timeline, the co-sell ladder, the offer checks, the fee lines, the buyer pre-check, the figure list, the reported claims and the footnote. A line starting "must:" applies to your reply.
retry_notenooptional; if present, your previous reply was unusable - follow it and reply with only the JSON object.

The rules the model applies

From the source skill, @samber/aws-marketplace-playbook - the only standard the run applies:

House rules

The replies

plan

Plan the listing: the fit verdict, the pricing model, the irreversible-choice register, one fix per failing listing check, the timeline to the first deal and this quarter's co-sell rung.

  1. summary - one sentence, at most 40 words: what stands between this listing and its first Marketplace deal today, and what the plan changes.
  2. verdict - an object: fit (exactly the facts' fit verdict: transact_with_commit, transact_no_commit or skip_for_now), why (one sentence naming the answers behind it).
  3. model - an object: pricing (exactly the facts' recommended model: contract, contract_consumption, subscription or free), why (one sentence: how the vendor sells, and that the model is fixed once published to Limited).
  4. register - one object per register row in the facts, in their order: choice (exactly the facts' choice: seller_account, pricing_model, dimension_api_name, custom_dimension, reseller_authorization, auto_renewal_optout or offer_terms), item (exactly the facts' item), decision (the facts' decision from the input, or "undecided"), owner ("unassigned" unless the input names a role), by ("unassigned", or a YYYY-MM-DD date the input gives).
  5. fixes - one object per listing check the facts mark fail, and any warn worth fixing: check (the page's check id), subject (the row's subject exactly as the facts write it, or "" when the row has none), change (one concrete sentence: what to change in the account, the model, the dimensions, the prices, the offer timing or the commitment language). Every fail row appears.
  6. timeline - the steps to the first deal, one object per step, in the facts' order: step (one sentence), wait (the wait, with † on every AWS figure, "reported" on every practitioner claim, or the page's own assumption named as such).
  7. cosell - an object: rung (exactly the facts' rung: ace, ftr, isv_accelerate or mpopp), next (one sentence: the condition that moves up a rung).
  8. footnote - exactly the footnote.
  9. caveats - at most 4 short strings: what the team must confirm; [] when there is nothing to say.
{
  "task": "plan",
  "summary": "one sentence",
  "verdict": { "fit": "transact_with_commit", "why": "one sentence" },
  "model": { "pricing": "contract", "why": "one sentence" },
  "register": [ { "choice": "dimension_api_name", "item": "seats (entitled)", "decision": "keep seats", "owner": "unassigned", "by": "unassigned" } ],
  "fixes": [ { "check": "dim_dup", "subject": "seats", "change": "one sentence" } ],
  "timeline": [ { "step": "one sentence", "wait": "Seller Operations reviews in 7-10 business days†" } ],
  "cosell": { "rung": "ace", "next": "one sentence" },
  "footnote": "† verify against the current AWS Marketplace Seller Guide before quoting it.",
  "caveats": [ ]
}

The page holds it to its own facts (check() in awreply.js; any warning makes the reply flagged), matching rows by the same composite identity its facts use: the reply answers the task it was sent; no URL with a scheme; no backticks; no implied AWS endorsement; a summary of at most 40 words; every AWS figure with its dagger and every practitioner claim labelled reported; the exact footnote; the page's fit verdict and pricing model (put back if moved); one register row per page row by choice plus item - two dimensions sharing a display name stay two rows - with the input's decision (put back), an owner of unassigned unless the input names a role and a date of unassigned or YYYY-MM-DD; a fix for every listing fail by check id plus subject, and only the page's rows; a timeline whose steps all carry a wait; the page's co-sell rung (put back) and a condition to move up.

offer

Review the private offer: the fee as the page computed it, every offer check that fails or warns, the terms, the buyer pre-check and a short message to the buyer's procurement contact.

  1. summary - one sentence, at most 40 words: what the offer contains, what must change before it goes out, and the fee.
  2. fee - an object: tier (exactly the facts' tier: public, new_under_1m, new_1m_10m, new_over_10m or renewal), rate (the facts' rate, each percentage with †), amount (exactly the facts' total fee, such as "$36,000.00"), net (exactly the facts' net).
  3. checks - one object per offer check the facts mark fail or warn, in their order: check (the page's check id), subject (the row's subject exactly as the facts write it, or ""), finding (one sentence), change (one concrete sentence: what to change in the draft before it goes out). [] when nothing fails or warns.
  4. terms - an object: installments (starting with exactly keep or fix, as the facts' must line says, then the reason), net_terms (what the net terms do to cash), renewal (the renewal flag and the auto-renewal advice), expiry (the expiry advice).
  5. buyer_precheck - one object per item of the facts' buyer pre-check, in their order: item (exactly the facts' item: aws_account, private_marketplace, invoice, active_agreement, commitment, currency or country), status (exactly the facts' status: ready, missing or unknown).
  6. message - a short note to the buyer's procurement contact: what the offer contains, then what they need to approve or confirm, each item on its own line. Separate the lines with a newline, written \n inside the JSON string - for example "The offer covers 36 months of Analyst seats for $2,400,000.00.\nThree annual installments of $800,000.00, net 30.\nPlease ask your Private Marketplace administrator to approve the product." Plain text; never promise a signature date earlier than the facts' realistic first deal; never claim AWS endorsement; no commit-burn language unless the facts say the badge is held for a fully-on-AWS product.
  7. footnote - exactly the footnote.
  8. caveats - at most 4 short strings: what to confirm before the offer goes out; [] when there is nothing to say.
{
  "task": "offer",
  "summary": "one sentence",
  "fee": { "tier": "renewal", "rate": "1.5%†", "amount": "$36,000.00", "net": "$2,364,000.00" },
  "checks": [ { "check": "inst_sum", "subject": "", "finding": "one sentence", "change": "one sentence" } ],
  "terms": { "installments": "keep - one sentence", "net_terms": "one sentence", "renewal": "one sentence", "expiry": "one sentence" },
  "buyer_precheck": [ { "item": "aws_account", "status": "unknown" } ],
  "message": "first line\nsecond line",
  "footnote": "† verify against the current AWS Marketplace Seller Guide before quoting it.",
  "caveats": [ ]
}

The page requires: the reply answers the task it was sent; the same text rules; the page's fee tier (put back), a rate carrying the tier's percentage with its dagger, and exactly the page's fee amount and net (put back - never recomputed); one row per offer check that fails or warns, by check id plus subject - an installment is its index plus its date - and no row the page does not flag; installments advice starting keep or fix as the page says; the renewal flag named when the page finds it missing; the buyer pre-check items with the page's statuses (put back); a message one item per line (an inline numbered list is caught), with no commit-burn language unless the badge is held and no signature date earlier than the page's realistic first deal.

Worked examples

Three runs from the page's free samples (invented products and buyers): the listing plan for a first listing that fails a lot, its offer review carrying that plan, and the offer review for a clean renewal moving to Marketplace. Each input is exactly what the page sends; each reply is the saved reply the sample replays, shipped unedited - but the long fields, strings and lists are shortened in this display only, and every cut is listed under the example. The last line is the page's own check of the full reply.

plan - A first listing that fails a lot

The page read it as: not yet - fails to fix first; stage Limited; fit transact_with_commit; model contract; 10 register rows; listing fails: seller_account, dim_dup (seats), price_window (seats 2027-01-11), first_offer_public, commit_burn; co-sell rung ace; the target date is late.

Input (facts is 12608 characters; the first 1396 are shown):

{
 "task": "plan",
 "listing": "Product: Ledgerline; pricing model: SaaS subscription; stage: Limited; another public paid product already listed: no\nPublic contract terms offered: 12, 36 months; free trial: 14 days\nSeller account: our main production account; hosting: fully on AWS; a third-party service touching customer data off AWS: no; Deployed on AWS badge: not applied; Partner Central and seller accounts linked: no\nBuyers: enterprise accounts with an AWS commitment; offer owner named: yes; engineering weeks available: 3; target date for the first Marketplace deal: 2026-11-12 (10 days from today, 2026-11-02)\nCompany country: United States; buyer countries: United States, Germany\nHow you sell today: Three-year enterprise contracts on annual seat tiers, invoiced yearly. Typical deal $250k to $900k. A few smaller customers pay by credit card.\nPublic price changes planned: seats $1,200.00 -> $1,320.00 on 2026-11-22; seats $1,320.00 -> $1,450.00 on 2027-01-11",
 "dimensions": "Dimensions (api name | display name | kind | unit | public price):\n- seats | Seats | entitled | user | $1,200.00\n- seats | Seat overage | metered | user-hour | $0.40\n- api_calls_v2 | API calls | metered | 1,000 calls | $2.50\n- enterprise_tier | Enterprise tier | custom-for-offers | tier | $0.00",
 "offer": "Buyer: Corvane Freight; buyer country: United States; existing customer moving the renewal to Marketplace: no; renewal flag set on the offer: no; replaces an active agreement: no\nTotal contract value: $850,000.00; term: 36 months; net terms: net 60 days; auto-renewal: on; offer expiry: 2026-11-23\nVia reseller CPPO: yes; wholesale price: $760,000.00; professional services included: no\nInstallments (index | date | amount): 1 | 2026-11-07 | $300,000.00; 2 | 2027-11-07 | $300,000.00; 3 | 2028-11-06 | $200,000.00\nBuyer-side notes: The buyer wants to burn their EDP commit with this purchase. Procurement needs vendor onboarding before the purchase order. Payment by invoice.",
 "carry": "",
 "facts": "The page's listing checks (5 fail, 7 warn, 3 pass, 3 info, 2 n/a); verdict: not yet - fails to fix first; rule | id | subject | status | evidence:\n- account | seller_account | - | fail | our main production account - it becomes seller of record forever and mixes Marketplace finance with infrastructure billing; register a fresh standalone account\n- model | model_fit | - | warn | a SaaS subscription while you sell contracts - installments and auto-renewal need a contract model; pick the model your sales team actually closes (SaaS contract) - already fixed at Limited: the wrong model means a new product\n- dimensions | dim_listed | - | pass | 4 dimensions listed\n- dimensions | dim_dup | seats | fail | seats is the API name of 2 dimensions (line 1, entitled; line 2, metered) - one API name per dimension, and it never changes\n- dimensions | dim_name | api_calls_v2 (metered) | warn | api_calls_v2 has a version suffix - dimension API names never change; pick the final name before you create it\n- dimensions | dim_custom | enterprise_tier (custom) | info | enterprise_tier - a custom dimension added for offers cannot be removed\n- dimensions | dim_metered | - | na | SaaS subscription meters usage\n- dimensions | dim_free | - | na | not the free model\n- model | public_terms | - | info | public terms of 12, 36 months are for the contract models\n- prices | price_change | seats 2026-11-22 |\n\n[... 11212 more characters cut from this display ...]"
}

Reply (shortened for display - see the cuts below):

{
 "task": "plan",
 "summary": "Ledgerline is registered on the main production account, sells contracts on a subscription model and drafts an offer while Limited; the plan moves the account, fixes the dimensions and holds the offer until Public.",
 "verdict": {
  "fit": "transact_with_commit",
  "why": "The product runs fully on AWS, the buyers are enterprises holding AWS commitments and an offer owner is named, so earn the badge before the first offer."
 },
 "model": {
  "pricing": "contract",
  "why": "The sales team closes three-year contracts on annual seat tiers, so a SaaS contract fits; the subscription model is already fixed at Limited, so that means a new product."
 },
 "register": [
  {
   "choice": "seller_account",
   "item": "our main production account",
   "decision": "our main production account",
   "owner": "unassigned",
   "by": "unassigned"
  },
  {
   "choice": "pricing_model",
   "item": "SaaS subscription",
   "decision": "SaaS subscription",
   "owner": "unassigned",
   "by": "unassigned"
  },
  {
   "choice": "dimension_api_name",
   "item": "seats (entitled)",
   "decision": "keep seats",
   "owner": "unassigned",
   "by": "unassigned"
  },
  {
   "choice": "dimension_api_name",
   "item": "seats (metered)",
   "decision": "keep seats",
   "owner": "unassigned",
   "by": "unassigned"
  },
  {
   "choice": "dimension_api_name",
   "item": "api_calls_v2 (metered)",
   "decision": "keep api_calls_v2",
   "owner": "unassigned",
   "by": "unassigned"
  }
 ],
 "fixes": [
  {
   "check": "seller_account",
   "subject": "",
   "change": "Register a fresh standalone seller account before any product is listed, so the main production account never becomes seller of record."
  },
  {
   "check": "dim_dup",
   "subject": "seats",
   "change": "Keep seats on the entitled dimension and give the metered overage row its own final lower-case API name before it is created, since API names never change."
  },
  {
   "check": "price_window",
   "subject": "seats 2027-01-11",
   "change": "Fold the two seats increases into one, or move the second one at least 90 days† after the first, because an increase freezes every further pricing update."
  },
  {
   "check": "first_offer_public",
   "subject": "",
   "change": "Hold the Corvane Freight offer until the listing passes Public review at a price above zero; a first product carries no private offer while Limited."
  },
  {
   "check": "commit_burn",
   "subject": "",
   "change": "Take the promise about the buyer's commitment out of the deal notes until the Deployed on AWS badge is granted, then let the buyer's AWS account team confirm any share."
  }
 ],
 "timeline": [
  {
   "step": "Link Partner Central with the AWS Marketplace seller account so offers attach to co-sell opportunities.",
   "wait": "no wait"
  },
  {
   "step": "Run the Limited listing at a reduced test price with allowlisted test accounts, then cancel every test subscription.",
   "wait": "one week - the page's own assumption"
  },
  {
   "step": "Submit the architecture diagram for the Deployed on AWS badge in Partner Central.",
   "wait": "AWS reviews the architecture first"
  },
  {
   "step": "Request Public with the real price.",
   "wait": "Seller Operations reviews in 7-10 business days†, longer on errors"
  },
  {
   "step": "Send the Corvane Freight offer after the buyer pre-check, once Public passes.",
   "wait": "no private offer before Public; plan 3-6 weeks† from registration to Public"
  }
 ],
 "cosell": {
  "rung": "ace",
  "next": "Move up to FTR once ACE opportunities carry real AWS consumption: an architecture and security review of roughly 2-4 weeks†."
 },
 "footnote": "† verify against the current AWS Marketplace Seller Guide before quoting it.",
 "caveats": [
  "Confirm with Seller Operations what happens to the subscription listing already in Limited.",
  "Confirm which legal entity the fresh seller account registers under."
 ]
}

Cut from this display only (the saved reply and the check use the full text): register (10 items, 5 shown); fixes (7 items, 5 shown).

What step 5 prints for it: seller_account | dim_dup (seats) | price_window (seats 2027-01-11) | first_offer_public | commit_burn | model_fit | dim_name (api_calls_v2 (metered))

The page's check: kept - nothing to flag

offer - A first listing that fails a lot

The input carries the saved plan (R.laneForm fills carry from the plan reply), as the page's handoff does, and the offer draft: fee $26,600.00 on tier new_under_1m, net $733,400.00; flagged offer checks: inst_model, inst_sum, auto_renewal.

Input (carry is 2433 characters; the first 1393 are shown; facts is 12337 characters; the first 1396 are shown):

{
 "task": "offer",
 "listing": "Product: Ledgerline; pricing model: SaaS subscription; stage: Limited; another public paid product already listed: no\nPublic contract terms offered: 12, 36 months; free trial: 14 days\nSeller account: our main production account; hosting: fully on AWS; a third-party service touching customer data off AWS: no; Deployed on AWS badge: not applied; Partner Central and seller accounts linked: no\nBuyers: enterprise accounts with an AWS commitment; offer owner named: yes; engineering weeks available: 3; target date for the first Marketplace deal: 2026-11-12 (10 days from today, 2026-11-02)\nCompany country: United States; buyer countries: United States, Germany\nHow you sell today: Three-year enterprise contracts on annual seat tiers, invoiced yearly. Typical deal $250k to $900k. A few smaller customers pay by credit card.\nPublic price changes planned: seats $1,200.00 -> $1,320.00 on 2026-11-22; seats $1,320.00 -> $1,450.00 on 2027-01-11",
 "dimensions": "Dimensions (api name | display name | kind | unit | public price):\n- seats | Seats | entitled | user | $1,200.00\n- seats | Seat overage | metered | user-hour | $0.40\n- api_calls_v2 | API calls | metered | 1,000 calls | $2.50\n- enterprise_tier | Enterprise tier | custom-for-offers | tier | $0.00",
 "offer": "Buyer: Corvane Freight; buyer country: United States; existing customer moving the renewal to Marketplace: no; renewal flag set on the offer: no; replaces an active agreement: no\nTotal contract value: $850,000.00; term: 36 months; net terms: net 60 days; auto-renewal: on; offer expiry: 2026-11-23\nVia reseller CPPO: yes; wholesale price: $760,000.00; professional services included: no\nInstallments (index | date | amount): 1 | 2026-11-07 | $300,000.00; 2 | 2027-11-07 | $300,000.00; 3 | 2028-11-06 | $200,000.00\nBuyer-side notes: The buyer wants to burn their EDP commit with this purchase. Procurement needs vendor onboarding before the purchase order. Payment by invoice.",
 "carry": "The listing plan's model: contract - The sales team closes three-year contracts on annual seat tiers, so a SaaS contract fits; the subscription model is already fixed at Limited, so that means a new product.\nThe listing plan's fit: transact_with_commit\nThe register (choice | item | decision | owner | by):\n- seller_account | our main production account | our main production account | unassigned | unassigned\n- pricing_model | SaaS subscription | SaaS subscription | unassigned | unassigned\n- dimension_api_name | seats (entitled) | keep seats | unassigned | unassigned\n- dimension_api_name | seats (metered) | keep seats | unassigned | unassigned\n- dimension_api_name | api_calls_v2 (metered) | keep api_calls_v2 | unassigned | unassigned\n- dimension_api_name | enterprise_tier (custom) | keep enterprise_tier | unassigned | unassigned\n- custom_dimension | enterprise_tier | keep enterprise_tier | unassigned | unassigned\n- reseller_authorization | CPPO for Corvane Freight | issue a single-buyer authorization at $760,000.00 wholesale | unassigned | unassigned\n- auto_renewal_optout | Corvane Freight | undecided | unassigned | unassigned\n- offer_terms | Corvane Freight | undecided | unassigned | unassigned\nThe plan's fixes:\n- seller_account: Register a fresh standalone seller account before any product is listed, so the main production account never becomes seller of record.\n- dim_dup\n\n[... 1040 more characters cut from this display ...]",
 "facts": "The page's listing checks (5 fail, 7 warn, 3 pass, 3 info, 2 n/a); verdict: not yet - fails to fix first; rule | id | subject | status | evidence:\n- account | seller_account | - | fail | our main production account - it becomes seller of record forever and mixes Marketplace finance with infrastructure billing; register a fresh standalone account\n- model | model_fit | - | warn | a SaaS subscription while you sell contracts - installments and auto-renewal need a contract model; pick the model your sales team actually closes (SaaS contract) - already fixed at Limited: the wrong model means a new product\n- dimensions | dim_listed | - | pass | 4 dimensions listed\n- dimensions | dim_dup | seats | fail | seats is the API name of 2 dimensions (line 1, entitled; line 2, metered) - one API name per dimension, and it never changes\n- dimensions | dim_name | api_calls_v2 (metered) | warn | api_calls_v2 has a version suffix - dimension API names never change; pick the final name before you create it\n- dimensions | dim_custom | enterprise_tier (custom) | info | enterprise_tier - a custom dimension added for offers cannot be removed\n- dimensions | dim_metered | - | na | SaaS subscription meters usage\n- dimensions | dim_free | - | na | not the free model\n- model | public_terms | - | info | public terms of 12, 36 months are for the contract models\n- prices | price_change | seats 2026-11-22 |\n\n[... 10941 more characters cut from this display ...]"
}

Reply (shortened for display - see the cuts below):

{
 "task": "offer",
 "summary": "The $850,000.00 Corvane Freight offer through a reseller pays a $26,600.00 fee (3%† plus 0.5%† CPPO), but its installments fall short of the total and need a contract model first.",
 "fee": {
  "tier": "new_under_1m",
  "rate": "3%† +0.5%† CPPO",
  "amount": "$26,600.00",
  "net": "$733,400.00"
 },
 "checks": [
  {
   "check": "inst_model",
   "subject": "",
   "finding": "The listing runs on SaaS subscription, and installments need a contract model.",
   "change": "Move the deal to the SaaS contract product before attaching an installment schedule, or bill it as one charge."
  },
  {
   "check": "inst_sum",
   "subject": "",
   "finding": "The three installments add up to $800,000.00 against an $850,000.00 total.",
   "change": "Raise the installments so they add up to $850,000.00, or lower the total to match the schedule the buyer agreed."
  },
  {
   "check": "auto_renewal",
   "subject": "",
   "finding": "Auto-renewal is on under SaaS subscription, which needs a contract model.",
   "change": "Turn auto-renewal off on this draft and set it again on the contract product, with the renewal price and the decision deadline."
  }
 ],
 "terms": {
  "installments": "fix - the schedule falls $50,000.00 short of the total and installments need a contract model.",
  "net_terms": "Net 60 sits inside Net 15 to Net 120†; AWS pays only after it collects, so the cash arrives 60 days after each invoice.",
  "renewal": "A new customer, so no renewal flag; leave auto-renewal off until the listing runs on a contract model.",
  "expiry": "The offer expires 2026-11-23 at 23:59:59 UTC; extend it rather than reissue it if Public review runs long."
 },
 "buyer_precheck": [
  {
   "item": "aws_account",
   "status": "unknown"
  },
  {
   "item": "private_marketplace",
   "status": "unknown"
  },
  {
   "item": "invoice",
   "status": "ready"
  },
  {
   "item": "active_agreement",
   "status": "unknown"
  },
  {
   "item": "commitment",
   "status": "missing"
  }
 ],
 "message": "The offer covers 36 months of Ledgerline for a total of $850,000.00, through the reseller your team chose.\nThe payment schedule is being corrected so the installments add up to the total.\nPlease confirm the 12-digit AWS account that will own the subscription. [... 114 more characters cut from this display]",
 "footnote": "† verify against the current AWS Marketplace Seller Guide before quoting it.",
 "caveats": [
  "Confirm the reseller is a registered paid seller before the authorization is published.",
  "Confirm the corrected schedule with the buyer before the offer goes out."
 ]
}

Cut from this display only (the saved reply and the check use the full text): buyer_precheck (7 items, 5 shown); message (373 characters, 259 shown).

What step 5 prints for it: new_under_1m $26,600.00 net $733,400.00 | inst_model | inst_sum | auto_renewal

The page's check: kept - nothing to flag

offer - A clean renewal moving to Marketplace

The input carries the saved plan (R.laneForm fills carry from the plan reply), as the page's handoff does, and the offer draft: fee $36,000.00 on tier renewal, net $2,364,000.00; flagged offer checks: none.

Input (facts is 9500 characters; the first 1398 are shown):

{
 "task": "offer",
 "listing": "Product: Northwind Analytics; pricing model: SaaS contract; stage: Public; another public paid product already listed: no\nPublic contract terms offered: 12, 24, 36 months; free trial: none\nSeller account: a fresh standalone account; hosting: fully on AWS; a third-party service touching customer data off AWS: no; Deployed on AWS badge: held; Partner Central and seller accounts linked: yes\nBuyers: enterprise accounts with an AWS commitment; offer owner named: yes; engineering weeks available: 2; target date for the first Marketplace deal: 2026-12-12 (40 days from today, 2026-11-02)\nCompany country: United States; buyer countries: United States, Canada\nHow you sell today: Annual and multi-year contracts on analyst seats and workspace tiers, invoiced annually. Typical deal $400k to $2.5M.\nPublic price changes planned: none",
 "dimensions": "Dimensions (api name | display name | kind | unit | public price):\n- analyst_seats | Analyst seats | entitled | user | $900.00\n- workspace_tier | Workspace | entitled | workspace | $12,000.00\n- premium_support | Premium support | custom-for-offers | year | $0.00",
 "offer": "Buyer: Fernhill Retail Group; buyer country: United States; existing customer moving the renewal to Marketplace: yes; renewal flag set on the offer: yes; replaces an active agreement: no\nTotal contract value: $2,400,000.00; term: 36 months; net terms: net 30 days; auto-renewal: on; offer expiry: 2026-11-22\nVia reseller CPPO: no; professional services included: no\nInstallments (index | date | amount): 1 | 2026-11-16 | $800,000.00; 2 | 2027-11-16 | $800,000.00; 3 | 2028-11-15 | $800,000.00\nBuyer-side notes: An existing customer moving its renewal to Marketplace. Their Private Marketplace administrator must approve the product. They expect the purchase to count toward their AWS commitment. Payment by invoice.",
 "carry": "The listing plan's model: contract - The team sells annual and multi-year contracts on seats and workspace tiers, and the SaaS contract it chose is fixed since Limited.\nThe listing plan's fit: transact_with_commit\nThe register (choice | item | decision | owner | by):\n- seller_account | a fresh standalone account | a fresh standalone account | unassigned | unassigned\n- pricing_model | SaaS contract | SaaS contract | unassigned | unassigned\n- dimension_api_name | analyst_seats (entitled) | keep analyst_seats | unassigned | unassigned\n- dimension_api_name | workspace_tier (entitled) | keep workspace_tier | unassigned | unassigned\n- dimension_api_name | premium_support (custom) | keep premium_support | unassigned | unassigned\n- custom_dimension | premium_support | keep premium_support | unassigned | unassigned\n- reseller_authorization | none in this draft | undecided | unassigned | unassigned\n- auto_renewal_optout | Fernhill Retail Group | undecided | unassigned | unassigned\n- offer_terms | Fernhill Retail Group | undecided | unassigned | unassigned",
 "facts": "The page's listing checks (0 fail, 0 warn, 14 pass, 1 info, 3 n/a); verdict: ready; rule | id | subject | status | evidence:\n- account | seller_account | - | pass | a fresh standalone account - seller of record, which cannot change once a product is listed\n- model | model_fit | - | pass | SaaS contract - what the page recommends from how you sell\n- dimensions | dim_listed | - | pass | 3 dimensions listed\n- dimensions | dim_dup | - | pass | every API name appears once\n- dimensions | dim_name | - | pass | every API name is lower case, without spaces or a version suffix\n- dimensions | dim_custom | premium_support (custom) | info | premium_support - a custom dimension added for offers cannot be removed\n- dimensions | dim_metered | - | pass | no metered dimension on the SaaS contract\n- dimensions | dim_free | - | na | not the free model\n- model | public_terms | - | pass | public terms of 12, 24, 36 months, from the 1, 12, 24 or 36 months† a contract offers; custom lengths go in private offers\n- prices | price_change | - | na | no change planned\n- trial | trial_days | - | na | no trial\n- billing | billing_channel | - | pass | nothing in how you sell points off Marketplace billing\n- offers | first_offer_public | - | pass | the listing is Public\n- commit | commit_burn | - | pass | \"count toward their AWS commitment\" - the badge is held and the product runs fully on AWS; never promise\n\n[... 8102 more characters cut from this display ...]"
}

Reply (shortened for display - see the cuts below):

{
 "task": "offer",
 "summary": "The $2,400,000.00 Fernhill Retail Group renewal is flagged as an existing customer moving to Marketplace, so it pays $36,000.00 at the 1.5%† renewal tier; every offer check holds.",
 "fee": {
  "tier": "renewal",
  "rate": "1.5%†",
  "amount": "$36,000.00",
  "net": "$2,364,000.00"
 },
 "checks": [],
 "terms": {
  "installments": "keep - three annual installments of $800,000.00 add up to the total inside the 36-month term.",
  "net_terms": "Net 30 sits inside Net 15 to Net 120†; AWS pays only after it collects, so cash lands 30 days after each invoice.",
  "renewal": "The renewal flag, Existing Customer Moving to AWS Marketplace, is set; auto-renewal is on, so set the renewal price, the maximum renewals and the decision deadline.",
  "expiry": "The offer expires 2026-11-22 at 23:59:59 UTC and can be extended if procurement needs more time."
 },
 "buyer_precheck": [
  {
   "item": "aws_account",
   "status": "unknown"
  },
  {
   "item": "private_marketplace",
   "status": "ready"
  },
  {
   "item": "invoice",
   "status": "ready"
  },
  {
   "item": "active_agreement",
   "status": "unknown"
  },
  {
   "item": "commitment",
   "status": "ready"
  }
 ],
 "message": "The renewal offer covers 36 months of Analyst seats and the Workspace tier for $2,400,000.00.\nThree annual installments of $800,000.00 on 2026-11-16, 2027-11-16 and 2028-11-15, on net 30.\nPlease ask your Private Marketplace administrator to approve the [... 178 more characters cut from this display]",
 "footnote": "† verify against the current AWS Marketplace Seller Guide before quoting it.",
 "caveats": [
  "Confirm whether the account already holds an active agreement for the product, which would need a replacement offer."
 ]
}

Cut from this display only (the saved reply and the check use the full text): buyer_precheck (7 items, 5 shown); message (430 characters, 252 shown).

What step 5 prints for it: renewal $36,000.00 net $2,364,000.00

The page's check: kept - nothing to flag

Build the input yourself

Load the engine in node (global.window = global; require("./awkit.js"); require("./awreply.js")), then:

const K = AwKit, R = AwReply;
const f = K.form(Object.assign(K.blank(), { name: "Acme Analytics", pricing: "contract", stage: "public", owner: "yes", buy_enterprise: "yes",
  dimensions: "seats | Seats | kind: entitled | user | $1,200",
  o_buyer: "Example Corp", o_country: "United States", o_tcv: "USD 1.0M", o_term: "12", o_installments: "2027-01-15 | $1,000,000" }));
// f.fit (transact_with_commit, transact_no_commit, skip_for_now), f.model.pick, f.register (choice + item),
// f.rows (the listing checks: id + subject), f.offerRows (the offer pre-check), f.fee (tier, rate, amount, net, lines),
// f.precheck (the buyer pre-check), f.cosell (rung, next), f.timeline (steps, realistic, late)
// K.registerCsv(f), K.checksCsv(f), K.feeCsv(f), K.board(f), K.applyPlan(f, planReply) - the exports
const b = K.buildInput(f, "offer");                          // or "plan" (R.laneForm(form, "offer", planReply) fills carry)
const wire = K.toWire(b.input);                              // POST this to /estimate and /run
// then: const reply = R.normalize(R.parseReply(job.output.output), "offer");
// R.check(reply, b)                                         // {status: kept or flagged, notes, fixed}

Get a token for the steps below from /tokens.html.

Step 1 — a tiny client helper

One function that sends JSON and unwraps the response. Paste a personal token from /tokens.html, or leave it empty and mint a guest token in step 2.

BASE="https://api.skillsafe.ai/v1/app-api"
TOKEN="YOUR_TOKEN"   # from https://aws-offer-desk.skillsafe.ai/tokens.html
api() { # api METHOD PATH [JSON-BODY]
  curl -s -X "$1" "$BASE$2" -H "Content-Type: application/json" \
    ${TOKEN:+-H "Authorization: Bearer $TOKEN"} ${3:+--data "$3"}
}

Step 2 — a token

A guest token is enough for /me and /estimate. A run is metered, so it needs a personal token: sign in on /tokens.html and copy it from there.

# a guest token (enough for /me and /estimate); a run needs your personal token from /tokens.html
TOKEN=$(curl -s -X POST "$BASE/guest" -H "Content-Type: application/json" -d '{"slug":"aws-offer-desk"}' | jq -r '.data.token')

Step 3 — GET /me

Who the token is (subject_type: user or guest) and the credit balance.

api GET /me | jq '.data | {subject_type, credits}' 

Step 4 — POST /estimate

Free. The body is the run input itself, the bare object (never wrapped in {"input": ...}). hold_credits is a reservation, not the price; min_credits is what the balance must cover; model_alias reads gpt-terra.

# input.json: the run input object - {"task": "plan", "listing": "Product: Ledgerline; pricing model: SaaS subscription; stage: Limited
...", "dimensions": "Dimensions (api name | display name | kind | unit | public price):
...", "offer": "Buyer: Corvane Freight; buyer country: United States
...", "carry": "", "facts": "The page's listing checks (...); verdict: not yet - fails to fix first; rule | id | subject | status | evidence:
- account | seller_account | - | fail | our main production account ..."}
# - see the input table
api POST /estimate "$(cat input.json)" | jq '.data | {hold_credits, min_credits, model, model_alias, markup_bps, warnings}' 

Step 5 — POST /run, then poll the job

Send an Idempotency-Key built from the lane and the input: a retry with the same key never bills twice. The page uses aws-offer-desk:<task>:<inputHash>:a<attempt> - the lane is part of the key, retry_note is left out of the hash - and its one automatic reformat retry is a new attempt number, named in the page as one extra run. A plan reply carries summary, verdict, model, register, fixes, timeline, cosell, footnote and caveats; an offer-lane reply carries summary, fee, checks, terms, buyer_precheck, message, footnote and caveats. The code below prints the gist of either: each fix of the plan, or the fee and each flagged offer check.

KEY="aws-offer-desk:$(jq -r .task input.json):$(shasum -a 256 input.json | cut -c1-16):a1"
JOB=$(curl -s -X POST "$BASE/run" -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" \
  -H "Idempotency-Key: $KEY" --data @input.json | jq -r '.data.job_id')
while :; do
  J=$(api GET "/jobs/$JOB"); S=$(echo "$J" | jq -r '.data.status')
  case "$S" in succeeded|failed|cancelled) break;; esac; sleep 2
done
echo "$J" | jq -r '.data.output.output' > reply.json   # one JSON object, the reply contract
echo "$J" | jq '.data | {charged_credits, truncated}'
jq -r 'if .task == "offer" then (.fee | "\(.tier) \(.amount) net \(.net)"), (.checks[] | "\(.check) \(.subject) \(.change)") 
       else (.fixes[] | "\(.check) \(.subject) \(.change)") end' reply.json

Step 6 — POST /run-stream

The same run as Server-Sent Events, with the same input.json. Browsers receive progress ticks rather than text deltas; the final done event carries the reply.

# Server-Sent Events: tick/delta events while the job runs, then a final event with the job
curl -N -s -X POST "$BASE/run-stream" -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" \
  -H "Idempotency-Key: aws-offer-desk:$(jq -r .task input.json):$(shasum -a 256 input.json | cut -c1-16):a1" --data @input.json