agentgates.aiVerified
Confidential computers for AI swarms: sealed VMs (Intel TDX) by the hour, USDC over x402.
Finance & CryptoPayments & trading
- Passed: Handshake
- Passed: Tools listed
- Passed: Updated recently
Median latency 514 ms · Last checked
Explore
Evidence for your next connection.
Independent measurements of remote MCP servers. Every observation has a timestamp.
Read-only probesMCP server · Payments & trading
Free back office for small businesses in Singapore and Southeast Asia: invoices, expenses, stock.
2 failed · 8 not measured
Median of successful probes over 7 days
Share of countable probes over 30 days, including daily rollups. Auth challenges are excluded.
Tool definitions returned by this server
Independent monitoring
Outdated check
Last connection measurement is over 24 hours old. Current availability is unknown.
Last checked| Test area | Passed | Failed | Unmeasured |
|---|---|---|---|
ConnectionCan a client connect and discover tools? | 5 | 0 | 0 |
Reliability & speedDoes it respond consistently and quickly? | 0 | 0 | 4 |
Access & authenticationWhat is required to use this server? | 1 | 0 | 2 |
Tool qualityAre the tool definitions usable by an agent? | 3 | 1 | 0 |
Publisher identityCan the publisher be traced to its source? | 3 | 1 | 2 |
Unmeasured means there is not enough evidence to decide. How these checks work
Start using Simpler.Asia
Choose your client, copy the configuration, then enable the server in your client’s MCP settings.
Run this command in your terminal, then restart Claude Code.
Claude Code
claude mcp add --transport http simpler-asia https://simpler.asia/mcpCapabilities
Showing 100 of 320 tools.
List your agent_note rows (newest first).
DiscoveredConfirm receipt of a note in your cross-tenant agent_note inbox — the ONLY edit a recipient holds, moving status unread to read under the registry's transition guard. Idempotent: re-confirming an already-read note is a no-op, and the subject/body stay fixed forever so a recipient can never rewrite what the sender said. NEXT: agent_note_list to see the rest of the inbox.
DiscoveredRead one agent_note by id.
DiscoveredSend a cross-tenant AI-to-AI message into another tenant's agent_note inbox — the door that turns a cross_tenant_comm consent grant into an actual reach. Both tenants must be KYB-approved and the recipient must hold an active grant (claims can narrow it further); every recipient-side failure refuses identically so the network can't be probed for who exists — only your OWN unapproved KYB answers distinctly, since that discloses nothing about the target. Your sender identity is injected server-side into from_tenant_id and cannot be spoofed. NEXT: agent_note_list on the recipient side confirms delivery.
DiscoveredYour prepaid wallet, period spend, caps, and runway as a compact line DSL (~8 lines). Requires auth (your balance is closed data). Read this BEFORE committing paid actions; pair with ai.estimate_cost to price a specific call first.
DiscoveredPrice an AI task BEFORE running it: provider passthrough at wholesale cost, no markup — the per-token platform fee is retired, so text/reasoning passes through at cost. Anonymous — open catalog includes open pricing. Returns integer micro-USD plus a USD total. NEXT: run the action, then ai.budget to watch the runway.
DiscoveredImport one bank account's statement as CSV text (CSV today, PDF later) so the forecast starts from the bank's own balance. Owner or admin only. Stores each line and each day's closing balance for the account named by bank_label, account_last4 and currency; uploading the same statement again adds nothing. Name an account the same way every time (the label is matched ignoring case). Before anything is stored, only the last 4 digits of the account number are kept, and runs of 6 or more digits, email addresses and phone numbers are cut from descriptions and counterparties; payee and payer names stay as the statement gives them until the workspace is closed, when they are emptied and the lines are kept as figures and dates. The columns are recognised from the header (date, balance, and one signed amount or separate withdrawal and deposit columns); no bank layout is verified yet, so check the mapping in the reply. The latest balance, if under 31 days old, becomes the forecast's starting cash, graded 'statement', in place of a typed balance for the same account (a typed account whose label ends in other last 4 digits and dated on or after the statement is added to it, and the sum is graded 'declared'; an older one is left out until it is saved again with today's date), and the stored forecast is rerun. Returns counts, statement_balance, the mapping used, balance_checks, and forecast.status with next_step; verify with forecast.read. Refuses with FORBIDDEN SIMPLER_BANKING_ROLE_REFUSED for any other role (ask an owner or admin); VALIDATION_ERROR SIMPLER_BANKING_ACCOUNT_LAST4_INVALID when account_last4 has fewer than 4 digits (send the last 4 as text); VALIDATION_ERROR SIMPLER_BANKING_STATEMENT_FIELDS_INVALID naming each bad field; CONFLICT SIMPLER_BANKING_ACCOUNT_LABEL_CONFLICT with saved_accounts when an account with the same account_last4 and currency is saved under another bank_label, before anything is stored, because one account under two names would count its balance twice (resend with the saved bank_label for that account, or with new_account: true for a different account); VALIDATION_ERROR SIMPLER_BANKING_STATEMENT_UNPARSEABLE with reason and the file's columns when the columns or dates cannot be read (send mapping with 1-based column numbers, or date_order); PAYLOAD_TOO_LARGE SIMPLER_BANKING_STATEMENT_TOO_LARGE over 2,000,000 bytes or 20,000 lines (export a shorter date range); SERVICE_UNAVAILABLE SIMPLER_BANKING_STATEMENT_WRITE_FAIL (retry the same upload).
DiscoveredList your brand_assets rows (newest first).
DiscoveredRead one brand_assets by id.
DiscoveredList your consent_grant rows (newest first).
DiscoveredRead one consent_grant by id.
DiscoveredCreate a contact and, in the same call, log its first note and/or first interaction — the composite first-touch door. Children validate before the contact write; if the contact commits but a child then fails, the error names the contact's id plus the exact call to finish it, never a silent orphan. A contact with the same email or phone already in this tenant refuses with CONFLICT. NEXT: contact_upsert merges into an existing contact instead of creating a duplicate.
DiscoveredDelete a contact you own.
DiscoveredList your contact rows (newest first).
DiscoveredAdd a note to a contact — the same door as the granular tool, extended only to accept contact_name, contact_email or contact_phone in place of contact_id. A name match must be unique: more than one hit refuses, listing the candidate ids; zero matches on every alias refuses with the runnable two-step call to create the contact first, then retry. NEXT: contact_upsert when no contact exists yet.
DiscoveredDelete a contact_note you own.
DiscoveredList your contact_note rows (newest first).
DiscoveredRead one contact_note by id.
DiscoveredRead one contact by id.
DiscoveredSearch your contact rows by text.
DiscoveredUpdate a contact you own (optionally compare-and-set via expected_updated_at).
DiscoveredCreate-or-merge a contact keyed on email-then-phone identity — the sync door the future desktop harness and ingest lanes write through. Matching probes email first, then phone; a hit MERGES only the fields you send (omitted fields keep their stored values, so a partial sync payload can never wipe a curated field), a miss creates. Status moves still obey the transition machine — an upsert cannot jump straight to vip. Accepts the same first-note/first-interaction composite children as contact_create.
DiscoveredPins one widget or a saved artifact onto the operator's home canvas. kind='widget' requires widget_key from the shipped enum; kind='artifact' requires content_ref (an assistant artifact id) — the door enforces whichever pair the kind demands, since the schema alone cannot express it. Rows are private to the creating user, even though teammates share the same tenant. NEXT: desk_surface_list reads the current layout.
DiscoveredDelete a desk_surface you own.
DiscoveredList your desk_surface rows (newest first).
DiscoveredRead one desk_surface by id.
DiscoveredMoves, resizes, or restacks a surface already on the desk canvas — x/y/w/h/z_index are real column writes, not a layout cache. The surface's identity is frozen here: kind, widget_key, and content_ref never change through this door, only via delete then re-create. NEXT: desk_surface_list to find the id to move.
DiscoveredFetch a document from a URL, classify its type (personal/medical/business/unknown) with keyword + PII-pattern heuristics (documents containing actual PII data are NEVER business), and return extracted text capped at 100K chars. Free. STREAMING SUPPORTED: send Accept: text/event-stream for started/fetched/classifying/completed phases.
DiscoveredCreate a canvas project from a Fabric.js toJSON serialization (canvas_json, required): every embedded data:image src is pulled out, hashed, and written to a tenant-scoped, content-addressed R2 key before the row is saved, so canvas_json never carries image bytes and identical images dedupe across projects. SVG, an unrecognized image type, an oversized embedded image, or a canvas still too large after externalization refuse the whole create — nothing is silently dropped. An optional thumbnail_data_url is externalized the same way into thumbnail_key; thumbnail_key and version are server-set and refuse if you pass them. NEXT: edit_project_update to autosave the same project as it changes.
DiscoveredDelete a edit_project you own.
DiscoveredList your edit_project rows (newest first).
DiscoveredRead one edit_project by id.
DiscoveredSave changes to an existing canvas project by id — the autosave door: send only the fields that changed (name, canvas_json, width, height, thumbnail_data_url), everything else on the row is left alone. A canvas_json you send goes through the same image-externalization and hygiene refusal as edit_project_create (embedded images become R2 keys; SVG, unrecognized types, oversized images, or a still-too-large canvas refuse the whole save). version is bumped by the door itself on every save and thumbnail_key is server-minted from thumbnail_data_url — both refuse if you pass them directly. NEXT: edit_project_read to confirm the saved version.
DiscoveredAggregate (count/sum) over your expense rows.
DiscoveredApproves a submitted expense claim — requires the admin or owner role, and only a 'pending' expense qualifies (CONFLICT otherwise). Stamps approved_by and approved_at with the caller and the current time; once approved, the row's content freezes for everyone except the approver themselves. NEXT: expense_reimburse (owner only) to pay it out.
DiscoveredCreate a expense_budget.
DiscoveredDelete a expense_budget you own.
DiscoveredList your expense_budget rows (newest first).
DiscoveredRead one expense_budget by id.
DiscoveredUpdate a expense_budget you own (optionally compare-and-set via expected_updated_at).
DiscoveredAdds a custom category to your tenant's category table — slug must be 1-50 chars of lowercase a-z, 0-9, '-' or '_' and unique per tenant (CONFLICT if taken). is_default is server-owned: supplying it at all refuses with VALIDATION_ERROR, and this door always creates with is_default 0 — the 9 seeded defaults exist only via expense_category_ensure_defaults. sort_order defaults to the next open slot when omitted.
DiscoveredDeletes a custom category — categories are tenant-shared, so any tenant member can remove one regardless of who created it; the 9 seeded defaults are undeletable and refuse with CONFLICT (default_category: true). Expenses already carrying the deleted category keep their category_id untouched and simply render as Uncategorized; nothing is reassigned or blocked.
DiscoveredSeeds the 9 canonical default categories (Food & Dining, Transport, Utilities, Shopping, Entertainment, Healthcare, Travel, Office & Supplies, Other) into your tenant, skipping any slug that already exists. Takes no arguments and is safe to call repeatedly or concurrently — it converges rather than duplicating. NEXT: expense_category_list to see the result.
DiscoveredList your expense_category rows (newest first).
DiscoveredRead one expense_category by id.
DiscoveredEdits a category's name, icon, color, or sort_order — slug and is_default are the category's fixed identity, and any attempt to change either refuses with VALIDATION_ERROR. color, when given, must be a #rrggbb hex value. Create a new category instead of trying to rename an existing slug.
DiscoveredLogs a new expense — amount_cents must be integer CENTS (a decimal string via `amount` also works, and `category` resolves to a category_id by name). A same-amount, same-UTC-day duplicate for the same user is refused as CONFLICT unless you resend identically with force: true. Status is honor-or-refuse: admin/owner creators land 'approved' by default (or may request it explicitly), everyone else always starts 'draft'. NEXT: expense_submit sends a draft into the approval queue.
DiscoveredDeletes an expense you created — draft and rejected rows only. Pending, approved, and reimbursed rows are approval-trail records and refuse with CONFLICT (approval_locked: true), and the lock is pinned into the delete itself so a status change racing your read still refuses instead of silently deleting. NEXT: expense_reject it first (admin+) if it must leave the books.
DiscoveredList your expense rows (newest first).
DiscoveredRead one expense by id.
DiscoveredCreate a expense_recurrence.
DiscoveredDelete a expense_recurrence you own.
DiscoveredList your expense_recurrence rows (newest first).
DiscoveredRead one expense_recurrence by id.
DiscoveredUpdate a expense_recurrence you own (optionally compare-and-set via expected_updated_at).
DiscoveredMarks an approved expense as paid out — restricted to the tenant owner because it moves real money, and only an 'approved' expense qualifies (CONFLICT otherwise). Stamps reimbursed_at, after which the row's content freezes unconditionally since the amount approved is the amount recorded as paid. Records the payout only — no funds move automatically.
DiscoveredRejects a submitted expense claim — requires the admin or owner role, and only a 'pending' expense qualifies (CONFLICT otherwise). rejection_reason (up to 500 chars) is optional but records why; rejected is terminal and never counts as spend, so a corrected claim is a fresh expense_create, never a resubmit.
DiscoveredSearch your expense rows by text.
DiscoveredSends your own draft expense into the approval queue — draft only; an already-submitted or resolved expense refuses CONFLICT, and only the expense's own creator may submit it. Nobody is notified automatically — approvers see it appear in the Team Claims view. NEXT: expense_approve or expense_reject (admin/owner).
DiscoveredEdits an expense's own fields only — status and approval stamps refuse with VALIDATION_ERROR here since those are transitions (expense_submit, expense_approve, expense_reject, expense_reimburse). amount_cents, if changed, must stay integer CENTS. Only the expense's own creator may edit it, and content freezes once the row is 'pending', reimbursed, or approved by someone else — draft, rejected, and your own self-approved rows stay editable.
DiscoveredRead the tenant's STORED forecast — the verify-read for this rail. view picks the answer, one of summary, monthly, variance, scenarios, weekly (default summary). summary: the outcome bands (label, growth_direction, probability_pct, final_month_revenue_low and _high, distress_risk_pct, path_count), the cash block, blend {yours_pct, peers_pct} (the share of the result from this business's own history versus businesses like it; null when shallow). monthly: per month revenue, costs and net at p10, p25, p50, p75 and p90 with distress_risk_pct, plus balance_p10 to balance_p90 and below_zero_cum_pct when a balance anchors the cash block, and the cash block. This release moved monthlyForecast out of the default answer into view 'monthly'. Every view carries view, dataMonths, history_months, shallow (true under 5 months of history) and CFO narration status, money formatted in the business's currency, named in the top-level currency field: the engine runs in USD and figures convert back at the rate pinned on the stored result (fx_rate_to_usd); before this release they were USD. Free. Refuses with VALIDATION_ERROR naming view when it is not in the list. The cash block is computed from a real bank balance plus each simulated path's revenue minus costs: with basis 'anchored' it carries provenance (the balance grade), balance_source ('statement': the latest balance on bank statements imported with banking.import_statement, used while under 31 days old in place of a typed balance for the same account; a typed account whose label ends in other last 4 digits, or the same digits in another currency, and dated on or after the statement, is added to it and provenance is then declared (an older one is left out: save it again with today's date); 'settings': entered on the Forecast page or saved by forecast.run_stored; 'upload_row': the cash row of an uploaded sheet), balance_as_of, runway_months_p10 and _p50 (null = no cash-out within the horizon), cash_out_pct, lowest_balance_p10, burn_rate_p50_per_month, cost_cut_to_survive_pct and health. With no balance dated within 45 days before the day the forecast was computed and before its first month, basis is 'unanchored': runway, cash-out and health are null, net-flow figures remain, and unavailable_reason plus next_step say how the owner adds today's balance. unavailable_reason 'forecast_out_of_date' means the forecast's months end before today or before the balance on record, so today's balance cannot start it: rebuild from recent months first (forecast.run_stored, or an upload on the Forecast page), as its next_step says. This release replaced the old cashFlow block (its runway started from an invented balance), removed the monthly cash median and renamed the monthly death-risk column to distress_risk_pct. The summary view also carries readiness, whether the books are ready for forecast.run_stored without running it: ready, months, first_period, last_period, min_months, gap_months, recent_gap_months, gap_window_months, months_with_receipts, refusal_code (null when ready, else SIMPLER_FORECAST_LEDGER_TOO_SHORT, SIMPLER_FORECAST_LEDGER_GAPS or SIMPLER_FORECAST_LEDGER_NO_RECEIPTS), message, next_step, base_currency, base_currency_source, legacy_paid_without_payments, fx_unconverted_currencies, bucket_timezone and excluded_period; when the books cannot be read just now readiness is null and readiness_unavailable {code SIMPLER_FORECAST_LEDGER_READ_FAIL, message, next_step} says to retry, and the forecast still returns. variance: the track record, earlier saved forecasts against what happened, worked out on each read and never stored. variance.rows has one row per month with both an actual and an earlier forecast: month, horizon_months (1 = the forecast whose data ended the month before), forecast_made_on, actual, p10, p50, p90, inside_80 (the actual landed between p10 and p90), error_pct ((actual - p50) / p50 x 100, null when p50 is not above zero) and fx_pinned (false = converted at today's rate because that saved forecast carries no rate of its own). variance.summary: n, inside_80_count, inside_80_pct, median_abs_pct_error, min_months and verdict, one of too_few, too_narrow, consistent, too_wide: too_few under 12 months, otherwise whether the share inside sits below, within or above what an 80% range should give over n months. Actuals are paid receipts from the books for a books forecast, or the latest upload's revenue for a sheet, in variance.currency; months with zero revenue count. When they cannot be read, variance is null and variance_unavailable {code SIMPLER_FORECAST_VARIANCE_READ_FAIL, message, next_step} says to retry. scenarios: what if, on the stored forecast. It reruns the stored months at the stored fx_rate_to_usd (never a live rate) and applies up to 3 driver sets to the same simulated paths; it stores nothing and calls no model. drivers is a flat list of {kind, start, end?, amount?, pct?, customer_ref?, set?}: set (1 to 3, default 1) says which compared set a driver belongs to, at most 20 in a set; kinds, dates and amounts as forecast.run describes, amounts in the stored forecast's currency; lose_customer takes customer_ref (an invoice client id from invoice_client_list; the books give its average monthly receipts over the 6 months before the first forecast month) or amount. With no drivers it answers the base alone. scenarios.base and each scenarios.compared entry carry set, drivers, floored_path_months (path-months a driver would have taken below zero, held at zero), months (revenue, costs and net at p10 to p90, financing, and balance_p10 to _p90 with below_zero_cum_pct when a balance anchors it) and a cash block; scenarios.lose_customer echoes each customer average used. Drivers change the money on the paths, not growth or closures (no knock-on effects). Refuses with VALIDATION_ERROR and simpler_code SIMPLER_FORECAST_DRIVER_INVALID (a field is wrong, an amount names another currency than the stored forecast's, or a start falls outside the forecast months: fix it as the field says), SIMPLER_FORECAST_DRIVER_LIMIT (more than 20 drivers in a set, or a set above 3: split the call) or SIMPLER_FORECAST_DRIVER_UNKNOWN_CUSTOMER (no such client: use an id from invoice_client_list, or send amount); drivers with a view other than scenarios or weekly, or cash_on with a view other than weekly, is a VALIDATION_ERROR; when the books cannot be read for a customer_ref it refuses with SERVICE_UNAVAILABLE SIMPLER_FORECAST_LEDGER_READ_FAIL: retry, or send amount. weekly: the next 13 weeks, Monday to Sunday in weekly.bucket_timezone, worked out from the books on each read (2000 seeded draws, the same answer all week for the same books; it stores nothing and calls no model), money in weekly.currency, the books' currency, which driver amounts are read in too. Days before weekly.model_start are the books' own money. From it on, open invoices (the 2000 largest; truncated_invoices counts the rest) are paid per their receipt timing, recurring invoices and expenses land on their schedule, and typical new invoices, cash sales and spend are drawn from the last 26 weeks and spread evenly over each week's days (resampled_flows_spread 'even_by_day'). weekly.weeks has week, start, end, recorded_days, receipts, payments and net at p10, p50 and p90, financing, and top_contributors (up to 5: kind, ref, label, direction, expected). With a stored balance dated within 31 days, rolled forward with the books' movements, each week also has balance_p10, _p50 and _p90 at Sunday's end, below_zero_pct, lowest_day and lowest_day_balance_p10 (the week's lowest end-of-day balance at p10 and its day); without one, weekly.balance is null and balance_reason plus next_step say how to add it. drivers (one set, no set field) apply as in scenarios, except that a dated one moves money on its day: a one-off lands whole on that day, and a hire, a loan_repayment, or a revenue_step or cost_step above zero with a start day lands its whole monthly amount on that day of each month through its end (the month's last day when the month is shorter; a day already past adds nothing), where cash_on lists it; a step below zero (a cut) and a month-only monthly driver are spread evenly over each month's days from their start, and a month-only one-off over its days from model_start, or whole on model_start when none of its days is left. View scenarios, which is monthly, spreads a dated monthly driver's first month by the days it covers. cash_on (YYYY-MM-DD, weekly only) adds weekly.cash_on: that day's balance at p10, p50 and p90, below_zero_pct and dated_items due on or before it. weekly refuses with VALIDATION_ERROR and simpler_code SIMPLER_FORECAST_CASH_ON_OUT_OF_RANGE when cash_on is outside the 13 weeks (send a date inside them) or SIMPLER_FORECAST_DRIVER_INVALID when a driver starts outside them or its amount names another currency than the books' (a row's amount the same way refuses with SIMPLER_FORECAST_AGENT_INPUT_INVALID); with SIMPLER_FORECAST_WC_NEEDS_CASH when cash_on is asked with no usable balance stored (save today's balance first, as its next_step says); and with SERVICE_UNAVAILABLE SIMPLER_FORECAST_WEEKLY_READ_FAIL when the books cannot be read (retry). view weekly also takes your own accounting system's rows, open_receivables, paid_history, open_payables and recurring, in the shapes and caps forecast.run describes (a row without currency is in weekly.currency): paid and past-due rows join the books' history for the receipt timing, the items join the books' own (the 2000 largest open invoices across both are drawn), a row in a currency with no rate is left out and named in fx_unconverted_currencies, weekly.accounting_inputs counts the rows used, and nothing is stored. The starting balance stays the stored one; to try another balance send the lists to forecast.run. The lists with a view other than weekly are a VALIDATION_ERROR; they refuse with PAYLOAD_TOO_LARGE SIMPLER_FORECAST_AGENT_INPUT_TOO_LARGE over a cap (send the largest rows up to it) and VALIDATION_ERROR SIMPLER_FORECAST_AGENT_INPUT_INVALID naming each wrong field (fix it as it says), before the compute count. The variance, scenarios and weekly views count toward 60 compute reads an hour per workspace, shared with the Forecast page and apart from forecast.run, checked after the drivers are validated (a scenarios or weekly start outside the forecast's months included) and a stored forecast is found (a NOT_FOUND is not counted); past it the call refuses with RATE_LIMITED, simpler_code SIMPLER_FORECAST_READ_COMPUTE_LIMITED and a next_step: try again within the hour, and views summary and monthly are not counted. No stored forecast yet → NOT_FOUND whose error carries the runnable forecast.run_stored call and the same readiness and readiness_unavailable keys: when readiness.ready is false, fix the books as its next_step says before calling run_stored, which would refuse with the same code. For a member or viewer token, which run_stored refuses, next_call is instead a sentence: ask a workspace owner or admin to run the forecast.
DiscoveredWHAT-IF / scenario lane: runs monthly data YOU supply through a seeded 5,000-path simulation of monthly revenue and costs; bands are simulation outputs, not calibrated probabilities. The paths are clustered into 3-7 outcome bands for the next 6 months, with per-month revenue and cost percentiles and deathRisk, the share of paths the engine marks closed (a distress signal, not a cash-out date). Amounts are in currency (major units, default USD): 1500 or "1500.00" - never cents. currency is one of 16 codes (USD, EUR, GBP, JPY, CNY, AUD, HKD, INR, SGD, MYR, IDR, THB, PHP, VND, BND, MMK); leave it out and USD is assumed, and the output says currency_assumed: true. The engine runs in USD; every output figure converts back into currency at fx_rate_to_usd (USD per 1 unit), named in the top-level currency field. Send cash_balance (the bank balance, negative for an overdraft) with cash_as_of (its date, YYYY-MM-DD, within 45 days) and the cash block adds runway, cash-out share, lowest balance and health from that balance plus each path's revenue minus costs (balance_source 'request'); without them it carries net-flow figures only and its next_step says what to send. Send declared_totals {revenue, costs}, the totals your own source shows for the months sent, and the sums are checked on the server; totals_check echoes them. history_months, shallow (true under 5 months) and blend {yours_pct, peers_pct} (null when shallow) say how much of the result is your own history. Earlier releases removed the per-band cash range and the per-month cash percentiles (they started from an invented balance) and interpreted_money.unit (the top-level currency field replaces it). Refuses with VALIDATION_ERROR and a fix per field: fewer than 2 or more than 120 rows, a date that is not YYYY-MM, revenue or costs sent in cents or as text that is not a number (an exponent such as 1e5, letters other than one currency code or symbol, or a comma that does not group thousands), an amount whose code or symbol names another currency than the one it is read in (currency; cash_balance is read in cash_currency when sent: send that currency, or the amount in it; never converted), no month with revenue, or a currency outside the list; SIMPLER_FORECAST_CASH_WITHOUT_DATE when cash_balance comes without cash_as_of (send both); SIMPLER_FORECAST_CASH_AS_OF_INVALID when cash_as_of is not a real YYYY-MM-DD date or comes without cash_balance; SIMPLER_FORECAST_GRID_TOTALS_MISMATCH when a declared total and the months' sum are more than 0.5% apart (correct the months or the total and resend). Refuses with SERVICE_UNAVAILABLE SIMPLER_FORECAST_FX_UNAVAILABLE when no rate for a non-USD currency is available: retry shortly, or pass USD amounts. Send drivers (at most 20) to ask what if: each is {kind, start, end?, amount?, pct?}, kind one of revenue_pct, cost_pct, revenue_step, cost_step, hire, one_off_in, one_off_out, lose_customer, loan_drawdown, loan_repayment, festive_month. start and end are YYYY-MM (the whole month) or YYYY-MM-DD (that day); revenue_pct, cost_pct, lose_customer and festive_month take months only; start must fall inside the forecast months, and end may be left out to run to the last one. amount is in currency (major units): per month for revenue_step and cost_step (negative for a cut), hire (the full monthly cost), loan_repayment and lose_customer (that customer's average monthly receipts; forecast.run reads no records, so customer_ref is refused here), and the whole sum for one_off_in, one_off_out and loan_drawdown, which hit one month or one day and take no end. In text, commas only group thousands ("4,000.00"); a currency code or symbol may lead or follow ("SGD 4,000") only when it is the currency the amount is read in, and one naming another currency is refused, never converted; no exponent or other letters. The same applies to every amount this tool takes. pct is -100 to 500, not zero, for revenue_pct, cost_pct and festive_month (a revenue_pct on one month you pick: your assumption, not learned seasonality). loan_drawdown and loan_repayment move the balance only, never revenue, costs or net. With drivers the answer adds base and scenario, each with monthly revenue, costs, net, financing and balance percentiles and a cash block, on the same simulated paths; drivers change money on those paths, not growth or closures (no knock-on effects). Refuses with VALIDATION_ERROR and simpler_code SIMPLER_FORECAST_DRIVER_INVALID (fix the field as it says; a start outside the 6 months after the last month sent refuses before anything runs or is counted), SIMPLER_FORECAST_DRIVER_LIMIT (more than 20 drivers: send fewer) or SIMPLER_FORECAST_DRIVER_UNKNOWN_CUSTOMER (customer_ref sent: send amount instead, or use forecast.read view scenarios). No forecast and nothing you send is stored, and your business records are NOT read (the one setting read is the workspace's time zone, to know today; like every tool call, the call itself is counted in usage): for YOUR stored forecast call forecast.run_stored; read the last stored result with forecast.read. cash_currency (optional, one of the same codes) names the currency of cash_balance when it is not currency; the balance converts into currency at the cached rate, and interpreted_money.cash_balance echoes both. Your own accounting system's rows (for example from a Xero, QuickBooks or Zoho connection) add the next 13 weeks: send any of open_receivables (at most 2,000: {client_ref?, issued_at?, due_date, amount, currency?}), paid_history (at most 5,000: {client_ref?, due_date, paid_at, amount?}), open_payables (at most 2,000: {vendor_ref?, due_date, amount, currency?}) and recurring (at most 100: {direction in or out, amount, currency?, cadence weekly, monthly or quarterly, next_date}); an empty list counts as sent. Dates are YYYY-MM-DD; amounts are major units of the row's currency (default currency), converted into currency at the cached rates; client_ref and vendor_ref are your own ids, at most 60 characters, and nothing is looked up by them. The answer then adds weekly, shaped like forecast.read view weekly: Monday to Sunday weeks in weekly.bucket_timezone (the workspace's time zone, the one setting read, to know today), 2000 seeded draws (the same rows give the same answer all week), receipts, payments and net at p10, p50 and p90, top_contributors, payables_counted and accounting_inputs (rows used per list), from those rows alone: no typical weekly sales or spend are drawn, so send regular money as recurring items. Open receivables are paid per their receipt timing, and receipt_timing lists the 50 largest with source, median_payment_date, paid_in_13_weeks_pct, paid_after_13_weeks_pct and never_paid_pct, plus sources and learned_from: the timing is learned from paid_history (source client or tenant), else it is the labelled prior (source prior). recurring in items fall due on each date and are paid on the same curve; open payables and recurring out items leave on their date. With cash_balance and cash_as_of within 31 days the weeks carry balance bands, below_zero_pct and lowest_day, and cash_on (YYYY-MM-DD inside the 13 weeks) adds weekly.cash_on: that day's balance at p10, p50 and p90 and the dated items due by then. drivers apply to the monthly forecast only. These refuse before anything runs: PAYLOAD_TOO_LARGE SIMPLER_FORECAST_AGENT_INPUT_TOO_LARGE when a list is over its cap (send the largest amounts, or the most recent paid_history, up to the cap); VALIDATION_ERROR SIMPLER_FORECAST_AGENT_INPUT_INVALID naming each wrong field (a date that is not YYYY-MM-DD, an amount not above zero, a ref over 60 characters, a key the row does not take, an amount whose code or symbol names another currency than the row's currency (or currency when the row sends none), issued_at after due_date or after today, paid_at after today, a recurring next_date before this week's Monday): fix each as it says; VALIDATION_ERROR naming cash_on when none of the four lists is sent, or cash_currency when cash_balance is not; SIMPLER_FORECAST_CASH_ON_OUT_OF_RANGE when cash_on is outside the 13 weeks (send a date inside them); SIMPLER_FORECAST_WC_NEEDS_CASH when cash_on comes without a cash_balance dated within 31 days (send one); SERVICE_UNAVAILABLE SIMPLER_FORECAST_FX_UNAVAILABLE when a row's currency or cash_currency has no rate (retry shortly, or send it in currency). Every call counts toward forecast.run's own 60 engine runs an hour per workspace, apart from forecast.read's views and the Forecast page: one for the monthly run and one more when the 13 weeks are drawn. Past it the call refuses with RATE_LIMITED, simpler_code SIMPLER_FORECAST_READ_COMPUTE_LIMITED and a next_step: try again within the hour. Free internal compute, ~50ms without the lists. Streaming-capable: send Accept: text/event-stream for phase progress (started/fitted/simulated/clustered/complete).
DiscoveredRun the tenant's REAL forecast from the books — cash receipts (recorded invoice payments by payment date, plus paid invoices with no recorded payment at their paid date, flagged) and expenses by expense date, monthly in the tenant's base currency, up to the last complete month (the current month is left out; months are the business's calendar months in its own time zone, set in sales settings, Asia/Singapore when unset, and the books block names it in bucket_timezone) and at most 36 months — through the one import pipeline. The result is STORED, replacing the forecast every member reads, so it is owner or admin only: any other role is refused with FORBIDDEN SIMPLER_FORECAST_IMPORT_ROLE_REFUSED before anything is read, saved, run or charged (ask an owner or admin to run it; forecast.read reads the forecast already stored). No arguments are needed. Optional cash_balance (the bank balance in cash_currency, negative for an overdraft), cash_currency (one of 16 codes) and cash_as_of (its date, YYYY-MM-DD) go together: they are saved as the workspace's declared bank balance, replacing the balance entered before, and then the forecast runs and starts its cash block from that balance. When more than one bank account is saved on the Forecast page, it refuses with CONFLICT SIMPLER_FORECAST_CASH_MULTI_ACCOUNT_REFUSED before anything is saved or run, because one balance would replace them all: ask the owner or an admin to update the balances on the Forecast page, or call without the three cash fields to run from the saved balances. It refuses with SERVICE_UNAVAILABLE SIMPLER_FORECAST_SETTINGS_READ_FAIL when the saved balances cannot be read (retry in a minute). The cash fields refuse like forecast.run's (SIMPLER_FORECAST_CASH_WITHOUT_DATE, SIMPLER_FORECAST_CASH_AS_OF_INVALID, VALIDATION_ERROR naming cash_currency when it is missing or outside the list, or cash_balance when its code or symbol names another currency than cash_currency, before anything is saved) and with SERVICE_UNAVAILABLE SIMPLER_FORECAST_FX_UNAVAILABLE when no rate for cash_currency is available (retry shortly, or send the balance in USD). A refusal after the balance was saved carries cash_balance_saved: true. Cost: no metered model cost. Returns the first-3-months outlook and the cash block (as forecast.read describes it) with money formatted in the business's currency, named in the top-level currency field: the engine runs in USD and figures convert back at the rate pinned on the stored result (fx_rate_to_usd); before this release they were USD. Verify with forecast.read before reporting done. Outlook rows carry distress_risk_pct, the share of paths the engine marks closed by that month (renamed this release from the old death-risk column); they no longer carry a cash median, which started from an invented balance. Runway and cash-out come only from forecast.read's cash block. Refuses with RATE_LIMITED after 5 runs an hour and with 402 when the workspace credit cannot cover the cost: when a forecast is already stored the error names forecast.read, so read that instead. The books block in the result names the base currency, gap months, converted amounts and any paid invoices counted without a recorded payment. Refuses with BAD_REQUEST: SIMPLER_FORECAST_LEDGER_TOO_SHORT when the books have fewer than 2 complete months of receipts or spend, SIMPLER_FORECAST_LEDGER_GAPS when 2 or more of the last 6 complete months have no receipts and no spend, and SIMPLER_FORECAST_LEDGER_NO_RECEIPTS when the books carry no receipts or the last complete month has none. Fix: Record payments against your invoices (invoice_payment_record) and log expenses so each month has activity, or upload a spreadsheet on the Forecast page instead. Then run again. To check first without spending a run, call forecast.read: its summary and its no-forecast error carry readiness with the same refusal_code.
DiscoveredAdd a product shadow to a cut-out (subject on transparency), cast from its alpha: kind drop, contact or reflection. angle 0..359 is the light direction counterclockwise from 3 o'clock (default 120; the shadow falls the other way); distance 0..200 px (unset: 3.5% of the subject's height, at least 12; the gap under the base for a reflection); blur 0..100 px (unset: 5% of the subject's height, at least 16); opacity 0..100 (default 65); transparent true (default) keeps the backdrop see-through, false fills background #RRGGBB (default #ffffff) behind the shadow. contact ignores angle and distance, reflection ignores angle. The result grows past the input by the shadow's reach (distance plus blur) so no visible shadow pixel is cut: output pad {left, top, right, bottom} gives the growth in input pixels and subject_box the subject in the result; clipped true means the growth was trimmed to stay within 4,194,304 pixels. Free: runs on the platform's own deterministic kernel, so the same input and arguments always return the same pixels. Takes PNG, JPEG, WebP, GIF or HEIC. Returns JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; images are stored in this workspace. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, data:image or an asset_key), SIMPLER_MEDIA_PIN_TOO_LARGE (under 20MB), SIMPLER_EDIT_SHADOW_NEEDS_CUTOUT (no transparent pixels: run image.cut_background and pass its asset_key), VALIDATION_ERROR with reason SIMPLER_EDIT_TOOL_ARGS_INVALID for a value out of range, SIMPLER_EDIT_SERVER_PIXELS_NEEDS_PNG (the file is not an image: send a PNG, JPEG, WebP, GIF or HEIC), SIMPLER_EDIT_SERVER_PIXELS_TOO_LARGE (at most 4,194,304 pixels: run image.convert with width and height 2048 first), SIMPLER_IMAGES_BINDING_ABSENT (this deployment has no Images binding yet, owner step O-2: send a standard PNG), SIMPLER_IMAGES_QUOTA_EXCEEDED or SIMPLER_IMAGES_LEGACY_SUBSCRIPTION (an owner account step: send a standard PNG meanwhile), SIMPLER_IMAGES_INPUT_UNREADABLE (send a PNG, JPEG, WebP, GIF or HEIC), SIMPLER_IMAGES_INPUT_TOO_LARGE (under 20,000,000 bytes and 100 megapixels), SIMPLER_IMAGES_TRANSFORM_FAILED (retry, or send a standard PNG); nothing is charged on any of them, and INSUFFICIENT_CREDITS or PAYMENT_REQUIRED only when the workspace is already overdrawn or past its spend cap (add credits or raise the cap). NEXT: pass images[0].asset_key as image_asset_key to the next image.* tool, or open download_url when the result carries one.
DiscoveredApply 1..10 adjustments to a photo in order, with the editor's own kernels: a one-op stack returns the same pixels as that tool in the editor. ops is a list of {op, params}; each op takes only its own params keys, and a key left out takes that op's default: brightness_contrast (brightness, contrast), exposure (stops), temperature_tint (temperature, tint), vibrance_saturation (vibrance, saturation), curves (rgb, red, green, blue), levels (in_black, in_white, levels_gamma, out_black, out_white), hsl_per_range (range, hue, saturation, luminance), sharpen (amount, radius, shadow_protect, highlight_protect), blur (radius), denoise (luminance_strength, chrominance_strength, radius), vignette (amount, midpoint, feather), grain (amount), black_and_white (red_weight, green_weight, blue_weight, tint_color), auto_tone (aggressive). output_format is png (the default, free), jpeg or webp ($0.0005 for the one Images encode, charged only when the result is delivered). Free for png output, $0.0005 for jpeg or webp: runs on the platform's own deterministic kernel, so the same input and arguments always return the same pixels. Takes PNG, JPEG, WebP, GIF or HEIC. Returns JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; images are stored in this workspace. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, data:image or an asset_key), SIMPLER_MEDIA_PIN_TOO_LARGE (under 20MB), VALIDATION_ERROR with reason SIMPLER_EDIT_TOOL_ARGS_INVALID for an unknown op, a key the op does not take, a value outside the op's range, levels with in_white not above in_black, or an output_format outside png, jpeg and webp, SIMPLER_EDIT_SERVER_PIXELS_NEEDS_PNG (the file is not an image: send a PNG, JPEG, WebP, GIF or HEIC), SIMPLER_EDIT_SERVER_PIXELS_TOO_LARGE (at most 4,194,304 pixels: run image.convert with width and height 2048 first), SIMPLER_IMAGES_BINDING_ABSENT (this deployment has no Images binding yet, owner step O-2: send a standard PNG), SIMPLER_IMAGES_QUOTA_EXCEEDED or SIMPLER_IMAGES_LEGACY_SUBSCRIPTION (an owner account step: send a standard PNG meanwhile), SIMPLER_IMAGES_INPUT_UNREADABLE (send a PNG, JPEG, WebP, GIF or HEIC), SIMPLER_IMAGES_INPUT_TOO_LARGE (under 20,000,000 bytes and 100 megapixels), SIMPLER_IMAGES_TRANSFORM_FAILED (retry, or send a standard PNG); nothing is charged on any of them, and INSUFFICIENT_CREDITS or PAYMENT_REQUIRED only when the workspace is already overdrawn or past its spend cap (add credits or raise the cap). NEXT: pass images[0].asset_key as image_asset_key to the next image.* tool, or open download_url when the result carries one.
Discovered[paid: $0.0005] Convert an image to PNG, JPEG or WebP, and optionally bound its size, with the Cloudflare Images service: a PNG, JPEG, WebP, GIF or HEIC goes in and the result is stored in this workspace. format is png, jpeg, webp (AVIF is not offered). quality (1-100, default 85) applies to jpeg and webp. width and height (16-4096 px) bound the result and fit picks how: scale-down (the default) shrinks to fit and never enlarges, contain fits inside and may enlarge, cover and crop fill the box and cut the overflow, pad fits inside and fills the rest with background. background (#RRGGBB) fills transparent pixels and padding; a JPEG, which has no transparency, is laid on white unless you name a colour. One call is one Images transformation at $0.0005, charged only when the result is stored; the preview is never charged. Returns JSON {images:[{url, asset_key, download_url?, width, height, content_type}], price, output: {format, bytes, quality?, input_format, input_bytes}, next} plus a preview; a result with no download_url says why in download_url_unavailable. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, data:image or an asset_key), VALIDATION_ERROR naming the field for a format outside the list, a quality with png, a size outside the range, a fit without width or height, or a background that is not #RRGGBB; SIMPLER_IMAGES_BINDING_ABSENT (this deployment has no Images binding yet, owner step O-2: send a standard PNG), SIMPLER_IMAGES_QUOTA_EXCEEDED or SIMPLER_IMAGES_LEGACY_SUBSCRIPTION (an owner account step: send a standard PNG meanwhile), SIMPLER_IMAGES_INPUT_UNREADABLE (send a PNG, JPEG, WebP, GIF or HEIC), SIMPLER_IMAGES_INPUT_TOO_LARGE (under 20,000,000 bytes and 100 megapixels), SIMPLER_IMAGES_TRANSFORM_FAILED (retry, or send a standard PNG); nothing is charged on any of them; SIMPLER_MEDIA_PIN_TOO_LARGE when the result is over 20MB (pass width and height, or pick jpeg or webp); INSUFFICIENT_CREDITS or PAYMENT_REQUIRED when the workspace cannot cover $0.0005 or is past its spend cap (add credits or raise the cap). NEXT: Pass images[0].asset_key as image_asset_key to image.adjust, image.marketplace_check or another image.* tool; a download_url, when the result carries one, works for 24 hours without a session.
Discovered[paid: $0.0005] Remove the background from a photo, returning the subject on transparency (BiRefNet on Cloudflare Images; when that cannot read the image, Bria's background remover runs instead at $0.018). Returns JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; images are stored in this workspace, never provider links, and a result with no download_url says why in download_url_unavailable. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, data:image or an asset_key), SIMPLER_MEDIA_PIN_TOO_LARGE (under 20MB), INSUFFICIENT_CREDITS (add credits). SIMPLER_EDIT_RESULT_UNDELIVERED means the charge was refunded: retry. NEXT: pass images[0].asset_key as image_asset_key to the next image.* tool, or open download_url when the result carries one.
DiscoveredAnalyze a photo with vision (subjects, defects, OCR text, composition) and return a description. Token-billed at the platform's vision rates. Returns JSON {text, price, next}. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, data:image or an asset_key), SIMPLER_MEDIA_PIN_TOO_LARGE (under 20MB), SIMPLER_MEDIA_UNKNOWN_TYPE (send PNG, JPEG or WebP), INSUFFICIENT_CREDITS (add credits). NEXT: act on the findings with image.inpaint, image.cut_background or image.upscale.
Discovered[paid: $0.06/image max] Generate 1-3 images from a text prompt on the first lane that delivers, tried in order: google/nano-banana-2.1 via the Cloudflare AI catalog ($0.06/image), then @cf/black-forest-labs/flux-2-dev via Cloudflare Workers AI, only when the first lane is down ($0.00/image). Each lane settles its own price per delivered image, so an image never costs more than the first lane's price, the credit a call must have available before it runs; price.charged_micro_usd in the result is the actual charge. Nano Banana keeps printed words (signs, labels, posters) legible; for a product photo the owner already has, image.variation with the photo as reference keeps the real product. Each image is stored in this workspace's image library and returned as JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; never a provider link. A result with no download_url says why in download_url_unavailable. Refuses: VALIDATION_ERROR on an empty prompt (describe the image), INSUFFICIENT_CREDITS (add credits), SIMPLER_IMAGE_PROMPT_REFUSED names the label (sexual, minor, real_person or other_unsafe): rewrite the prompt without it, for example no real person's name or likeness; SIMPLER_IMAGE_PROMPT_CHECK_UNAVAILABLE (the safety check is down, nothing charged: retry later); SIMPLER_IMAGE_OUTPUT_FLAGGED (the output failed the content check and was refunded: change the prompt); SIMPLER_IMAGE_OUTPUT_SCAN_UNAVAILABLE (outputs cannot be checked right now, nothing charged: retry later). A partial result lists refunded outputs in output.withheld; SIMPLER_IMAGE_RESULT_UNDELIVERED means nothing was charged: retry. For canvas-sized generation inside an edit session use image.generate_for_edit. NEXT: pass images[0].asset_key as image_asset_key to image.variation or image.upscale.
Discovered[paid: $0.06] Generate an image from a text prompt (google/nano-banana-2.1) sized for editing. Returns JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; images are stored in this workspace, never provider links, and a result with no download_url says why in download_url_unavailable. Refuses: VALIDATION_ERROR on an empty prompt (describe the image), SIMPLER_IMAGE_PROMPT_REFUSED names the label (sexual, minor, real_person or other_unsafe): rewrite the prompt without it, for example no real person's name or likeness; SIMPLER_IMAGE_PROMPT_CHECK_UNAVAILABLE (the safety check is down, nothing charged: retry later); SIMPLER_IMAGE_OUTPUT_FLAGGED (the output failed the content check and was refunded: change the prompt); SIMPLER_IMAGE_OUTPUT_SCAN_UNAVAILABLE (outputs cannot be checked right now, nothing charged: retry later), INSUFFICIENT_CREDITS (add credits). SIMPLER_EDIT_RESULT_UNDELIVERED means the charge was refunded: retry. NEXT: pass images[0].asset_key as image_asset_key to image.inpaint, image.upscale or image.style_transfer.
DiscoveredMatch a pasted subject's light and colour to its scene: a Reinhard colour transfer in CIELAB moves the subject's mean and spread toward the scene's, weighted by the subject mask, at strength 0..100 (default 80). Pixels outside the mask never change. Pass the composite plus a subject mask (white = subject, black = scene). Free: runs on the platform's own deterministic kernel, so the same input and arguments always return the same pixels. Takes PNG, JPEG, WebP, GIF or HEIC. Returns JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; images are stored in this workspace. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, data:image or an asset_key), SIMPLER_MEDIA_PIN_TOO_LARGE (under 20MB), SIMPLER_EDIT_HARMONIZE_MASK_EMPTY (the mask selects no visible pixel: paint the subject white), SIMPLER_EDIT_HARMONIZE_NO_BACKGROUND (the mask covers everything: pass the whole composite with only the subject white), SIMPLER_EDIT_SERVER_PIXELS_NEEDS_PNG (the file is not an image: send a PNG, JPEG, WebP, GIF or HEIC), SIMPLER_EDIT_SERVER_PIXELS_TOO_LARGE (at most 4,194,304 pixels: run image.convert with width and height 2048 first), SIMPLER_IMAGES_BINDING_ABSENT (this deployment has no Images binding yet, owner step O-2: send a standard PNG), SIMPLER_IMAGES_QUOTA_EXCEEDED or SIMPLER_IMAGES_LEGACY_SUBSCRIPTION (an owner account step: send a standard PNG meanwhile), SIMPLER_IMAGES_INPUT_UNREADABLE (send a PNG, JPEG, WebP, GIF or HEIC), SIMPLER_IMAGES_INPUT_TOO_LARGE (under 20,000,000 bytes and 100 megapixels), SIMPLER_IMAGES_TRANSFORM_FAILED (retry, or send a standard PNG); nothing is charged on any of them, and INSUFFICIENT_CREDITS or PAYMENT_REQUIRED only when the workspace is already overdrawn or past its spend cap (add credits or raise the cap). NEXT: pass images[0].asset_key as image_asset_key to the next image.* tool, or open download_url when the result carries one.
Discovered[paid: $0.06] Remove or replace a masked region of a photo (Nano Banana repaints it in context; the result is the original with only the mask, grown and feathered, changed). White mask pixels = edit region. Returns JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; images are stored in this workspace, never provider links, and a result with no download_url says why in download_url_unavailable. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, data:image or an asset_key), SIMPLER_MEDIA_PIN_TOO_LARGE (under 20MB), SIMPLER_EDIT_FILL_MASK_EMPTY (the mask selects nothing: paint the object white), SIMPLER_IMAGE_PROMPT_REFUSED names the label (sexual, minor, real_person or other_unsafe): rewrite the prompt without it, for example no real person's name or likeness; SIMPLER_IMAGE_PROMPT_CHECK_UNAVAILABLE (the safety check is down, nothing charged: retry later); SIMPLER_IMAGE_OUTPUT_FLAGGED (the output failed the content check and was refunded: change the prompt); SIMPLER_IMAGE_OUTPUT_SCAN_UNAVAILABLE (outputs cannot be checked right now, nothing charged: retry later), INSUFFICIENT_CREDITS (add credits). SIMPLER_EDIT_RESULT_UNDELIVERED means the charge was refunded: retry. NEXT: pass images[0].asset_key as image_asset_key to the next image.* tool, or open download_url when the result carries one.
DiscoveredCheck a product photo against a marketplace's listing-image rules before the seller uploads it: shopee, lazada or tiktok_shop. Free and read-only: the platform's own deterministic check, which stores nothing, calls no marketplace and uploads nothing. Returns JSON {images: [], price ($0.00), output: {verdict (pass, fail or partial), failed, advice, skipped, file: {width, height, format, bytes}, rules: [{rule, level (required or recommended), status (pass, fail or skipped), expected, measured, fix, reason, source, checked, note}]}, next}. It checks the minimum and maximum size, the square shape, the file type, the file size and a white background; each rule names its source page and the day it was read, and a figure read through search results says UNVERIFIED in its note. The background is read from the pixels, up to 4,194,304 pixels (for example 2048x2048); a JPEG, WebP, GIF, HEIC or interlaced PNG is read through the Images binding at no charge. That one rule is skipped, with every other verdict kept, for a larger image (reason SIMPLER_EDIT_SERVER_PIXELS_TOO_LARGE: run image.convert with width and height 2048, then check the copy), or with the binding's reason when it cannot read the image: SIMPLER_IMAGES_BINDING_ABSENT when this deployment has no Images binding yet (owner step O-2: send a standard PNG copy to check it), or SIMPLER_IMAGES_QUOTA_EXCEEDED, SIMPLER_IMAGES_LEGACY_SUBSCRIPTION, SIMPLER_IMAGES_INPUT_UNREADABLE, SIMPLER_IMAGES_INPUT_TOO_LARGE or SIMPLER_IMAGES_TRANSFORM_FAILED, each with its fix in the rule. Added text and watermarks are skipped with reason SIMPLER_EDIT_MARKETPLACE_TEXT_CHECK_UNAVAILABLE or SIMPLER_EDIT_MARKETPLACE_WATERMARK_CHECK_UNAVAILABLE until the vision check lands (look at the image yourself). Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, data:image or an asset_key), SIMPLER_MEDIA_PIN_TOO_LARGE (under 20MB); VALIDATION_ERROR for a platform outside the list; VALIDATION_ERROR whose reason is SIMPLER_MEDIA_EMPTY, SIMPLER_MEDIA_UNKNOWN_TYPE, SIMPLER_MEDIA_KIND_NOT_ALLOWED, SIMPLER_MEDIA_SVG_REFUSED or SIMPLER_MEDIA_DIMENSIONS_UNREADABLE (send a JPEG, PNG, WebP, GIF or HEIC) or SIMPLER_MEDIA_PNG_CORRUPT (save it again as a standard PNG); for a HEIC only, the binding's reason when it cannot read the size (SIMPLER_IMAGES_BINDING_ABSENT while this deployment has no Images binding: send a JPEG or PNG copy); and INSUFFICIENT_CREDITS or PAYMENT_REQUIRED only when the workspace is already overdrawn or past its spend cap (add credits or raise the cap). NEXT: apply each failed rule's fix, for example image.add_shadow with transparent false and background #ffffff for a white background, or image.convert with format jpeg (and width and height for a size rule) for a file type, size or file-size failure, and check again.
Discovered[paid: $0.03] Replace a photo's background with a generated scene (Bria FIBO Edit). With no mask, the subject is cut out first (plus [paid: $0.0005]) and the original subject is laid back over the new scene; or pass a mask covering the background (white = background). Returns JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; images are stored in this workspace, never provider links, and a result with no download_url says why in download_url_unavailable. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, data:image or an asset_key), SIMPLER_MEDIA_PIN_TOO_LARGE (under 20MB), VALIDATION_ERROR on an empty prompt (describe the scene), SIMPLER_EDIT_BACKDROP_NO_SUBJECT (no mask and no clear subject: pass a background mask), SIMPLER_IMAGE_PROMPT_REFUSED names the label (sexual, minor, real_person or other_unsafe): rewrite the prompt without it, for example no real person's name or likeness; SIMPLER_IMAGE_PROMPT_CHECK_UNAVAILABLE (the safety check is down, nothing charged: retry later); SIMPLER_IMAGE_OUTPUT_FLAGGED (the output failed the content check and was refunded: change the prompt); SIMPLER_IMAGE_OUTPUT_SCAN_UNAVAILABLE (outputs cannot be checked right now, nothing charged: retry later), INSUFFICIENT_CREDITS (add credits). SIMPLER_EDIT_RESULT_UNDELIVERED means the charge was refunded: retry. NEXT: pass images[0].asset_key as image_asset_key to the next image.* tool, or open download_url when the result carries one.
Discovered[paid: $0.03] Replace a photo's sky (Bria FIBO Edit). Pass a mask covering the sky (white = sky). Returns JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; images are stored in this workspace, never provider links, and a result with no download_url says why in download_url_unavailable. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, data:image or an asset_key), SIMPLER_MEDIA_PIN_TOO_LARGE (under 20MB), SIMPLER_IMAGE_PROMPT_REFUSED names the label (sexual, minor, real_person or other_unsafe): rewrite the prompt without it, for example no real person's name or likeness; SIMPLER_IMAGE_PROMPT_CHECK_UNAVAILABLE (the safety check is down, nothing charged: retry later); SIMPLER_IMAGE_OUTPUT_FLAGGED (the output failed the content check and was refunded: change the prompt); SIMPLER_IMAGE_OUTPUT_SCAN_UNAVAILABLE (outputs cannot be checked right now, nothing charged: retry later), INSUFFICIENT_CREDITS (add credits). SIMPLER_EDIT_RESULT_UNDELIVERED means the charge was refunded: retry. NEXT: pass images[0].asset_key as image_asset_key to the next image.* tool, or open download_url when the result carries one.
Discovered[paid: $0.0005] Mask the side of the subject/background cut that pixel (x, y) falls on (BiRefNet on Cloudflare Images: the main subject, or everything behind it; not one object among several) and return the mask image. Returns JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; images are stored in this workspace, never provider links, and a result with no download_url says why in download_url_unavailable. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, data:image or an asset_key), SIMPLER_MEDIA_PIN_TOO_LARGE (under 20MB), VALIDATION_ERROR with reason SIMPLER_EDIT_TOOL_ARGS_INVALID when x or y is missing or outside 0..16384 (take the point from the user, in the image's pixels), INSUFFICIENT_CREDITS (add credits). SIMPLER_EDIT_RESULT_UNDELIVERED means the charge was refunded: retry. NEXT: pass images[0].asset_key as mask_asset_key to image.inpaint, image.replace_sky or image.replace_background.
Discovered[paid: $0.06] Apply a style to a photo while preserving the content (Nano Banana 2.1, told to change only the look). Returns JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; images are stored in this workspace, never provider links, and a result with no download_url says why in download_url_unavailable. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, data:image or an asset_key), SIMPLER_MEDIA_PIN_TOO_LARGE (under 20MB), VALIDATION_ERROR on an empty prompt (name the look), SIMPLER_IMAGE_PROMPT_REFUSED names the label (sexual, minor, real_person or other_unsafe): rewrite the prompt without it, for example no real person's name or likeness; SIMPLER_IMAGE_PROMPT_CHECK_UNAVAILABLE (the safety check is down, nothing charged: retry later); SIMPLER_IMAGE_OUTPUT_FLAGGED (the output failed the content check and was refunded: change the prompt); SIMPLER_IMAGE_OUTPUT_SCAN_UNAVAILABLE (outputs cannot be checked right now, nothing charged: retry later), INSUFFICIENT_CREDITS (add credits). SIMPLER_EDIT_RESULT_UNDELIVERED means the charge was refunded: retry. NEXT: pass images[0].asset_key as image_asset_key to the next image.* tool, or open download_url when the result carries one.
Discovered[paid: $0.005] Upscale a photo to about 4 megapixels (Pruna P-Image Upscale). Returns JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; images are stored in this workspace, never provider links, and a result with no download_url says why in download_url_unavailable. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, data:image or an asset_key), SIMPLER_MEDIA_PIN_TOO_LARGE (under 20MB), INSUFFICIENT_CREDITS (add credits). SIMPLER_EDIT_RESULT_UNDELIVERED means the charge was refunded: retry. NEXT: pass images[0].asset_key as image_asset_key to the next image.* tool, or open download_url when the result carries one.
Discovered[paid: $0.06/image] Generate 1-3 new images from a reference image guided by a prompt (google/nano-banana-2.1, which keeps the reference's product, logo and printed words when the prompt says to): a new scene, background, lighting or layout for the same subject. Pass image_asset_key (an earlier result) or image_url (public https or data:image) plus a prompt describing the change. Each image is stored in this workspace's image library and returned as JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; never a provider link. A result with no download_url says why in download_url_unavailable. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass one image input), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, data:image or an asset_key), VALIDATION_ERROR on an empty prompt, INSUFFICIENT_CREDITS (add credits), SIMPLER_IMAGE_PROMPT_REFUSED names the label (sexual, minor, real_person or other_unsafe): rewrite the prompt without it, for example no real person's name or likeness; SIMPLER_IMAGE_PROMPT_CHECK_UNAVAILABLE (the safety check is down, nothing charged: retry later); SIMPLER_IMAGE_OUTPUT_FLAGGED (the output failed the content check and was refunded: change the prompt); SIMPLER_IMAGE_OUTPUT_SCAN_UNAVAILABLE (outputs cannot be checked right now, nothing charged: retry later). A partial result lists refunded outputs in output.withheld. To edit a SPECIFIC region use image.inpaint; for whole-image restyle use image.style_transfer. NEXT: pass images[0].asset_key to image.upscale or image.cut_background.
DiscoveredLog an interaction — call, meeting, email, message, visit, payment, or other — against a contact. The ONE activity-logging door every lane funnels through, so the contact's last_contact_at freshness bumps no matter which surface logged the touch; the bump never moves backward, so importing older history can never regress a fresher touch. Accepts contact_name, contact_email or contact_phone in place of contact_id. NEXT: contact_read to see the bumped last_contact_at.
DiscoveredList your interaction rows (newest first).
DiscoveredRead one interaction by id.
DiscoveredCreate-or-update a interaction keyed on source_ref.
Discovered[REQUIRES APPROVAL] Retires a dilution fact row — the only correction lane (adjustments are append-only; amounts are never edited). If the retired row was a credit that settled the invoice, the status rolls from 'paid' back to 'sent' and paid_at clears; the remaining balance and the derived 'written_off' state re-derive from the live rows immediately. An agent's call parks here for human approval. NEXT: invoice_adjustment_record to re-record the correct amount.
DiscoveredList your invoice_adjustment rows (newest first).
DiscoveredRead one invoice_adjustment by id.
Discovered[REQUIRES APPROVAL] Records a credit note, write-off or post-issue discount on an issued invoice. A credit_note is numbered from the credit note series, holds its tax at the invoice's rate and takes reason_code and credit_outcome: reduce_balance (default) lowers what is owed; refunded_outside records a refund the business made outside Simpler, moves no money and is how a 'paid' invoice gets one. write_off and discount_post_issue need a 'sent' invoice. Money is integer CENTS (amount_cents) or a decimal STRING (amount); caps: see preconditions. Refuses a draft, a cancelled invoice, an amount over its cap and dispute_settlement; each refusal names the fix. An agent's call parks for approval. NEXT: invoice_read.
DiscoveredCreate a invoice_client.
DiscoveredDelete a invoice_client you own.
Discovered[REQUIRES APPROVAL] Asserts that a billing client IS an on-platform tenant (or pass counterparty_tenant_id null to unlink) and retro-stamps that client's existing invoices with the link in the same call — invoices_stamped in the result is the count. Linking requires the counterparty's 'cross_tenant_settlement' consent AND a live network relationship (an active match or a deal thread); unlinking passes neither gate, since it withdraws your own assertion rather than making a new one. NEXT: invoice_client_read to read back what linked.
DiscoveredList your invoice_client rows (newest first).
DiscoveredRead one invoice_client by id.
DiscoveredSearch your invoice_client rows by text.
DiscoveredUpdate a invoice_client you own (optionally compare-and-set via expected_updated_at).
DiscoveredCreate-or-update a invoice_client keyed on email.
DiscoveredONE call creates the whole document: client_name + `items` [{description, quantity, unit_price}], each price as written ("5000.00"). It claims the next invoice number and writes every line; client_name finds or creates the client. A 'paid' birth needs items in the same call. tax_scheme sg_gst or my_sst taxes each line by its tax_code (refusals: see tax_scheme). When present, the second content block is the invoice as copy-ready text, shown with a Copy button where the host draws MCP Apps; if the user says they cannot see the invoice, give them that block word for word; share:true adds a client link, only when the user asks for one. NEXT: invoice_payment_record to record money-in.
Discovered[REQUIRES APPROVAL] Issues a numbered debit note that raises what an issued ('sent' or 'paid') invoice is owed: an extra charge, or a price or tax correction. It is numbered from the debit note series and adds amount_cents (or a decimal STRING amount), tax at the invoice's rate inside, in its currency; a 'paid' invoice goes back to 'sent' until the rest is paid. Refuses a draft (change its lines instead), a cancelled invoice, an invoice with no lines, another currency, and a paid invoice with no payment recorded (record it first with invoice_payment_record); every refusal names the call to send. Never deleted: undo one with a credit_note. An agent's call parks for approval. Nothing is sent. NEXT: invoice_read.
DiscoveredGet one issued invoice ready to give the client, in one call: the document, the client link (made if it has none), the PDF (printed or the stored one) and the prepared message (invoice_message_prepare's words, with the link). ready lists the parts made; missing gives each other part's reason_code, reason and next_call. A turned-off link is never re-issued: invoice_share action 'rotate' waits for the owner's approval. NOTHING IS SENT. Refusals (CONFLICT): draft SIMPLER_SALES_MESSAGE_NOT_ISSUED (issue it with invoice_update status 'sent'); cancelled; written off; SIMPLER_SALES_MESSAGE_PURPOSE_UNFIT (send its next_call). Read-only token or viewer: FORBIDDEN. Free.
DiscoveredAdds one line item to an invoice. unit_price_cents is integer CENTS, 0 or more ($15.00 → 1500); quantity is 0.001 to 1,000,000 with up to 3 decimals (1.5 hours → 1.5); unit is an optional label (hr, kg); discount_bps is an optional line discount (10% → 1000). quantity × unit_price_cents is at most 10,000,000,000. Refuses a negative price (send discount_bps instead), a 'paid' parent (retire the payment first) and a 'cancelled' parent (issue a new invoice). Re-derives the invoice's paid/sent status. tax_code is the line's code under the document's tax_scheme, refused on a document without a scheme. NEXT: invoice_read to see the updated total.
DiscoveredSoft-deletes a line item — it leaves the invoice and its derived total immediately, but the row is retired for audit history, never removed. expected_updated_at is required: the line's updated_at from your read (invoice_item_read); without it the removal is refused and the refusal names the current value. Refuses when the parent invoice is 'paid' (ledger-locked: retire the payment first) or 'cancelled' (terminal: its lines stay as issued), and with CONFLICT when the line changed since that read (read it again). If retiring the line settles the remaining balance on a partially-paid invoice, the parent re-derives to 'paid' by itself. NEXT: invoice_read to see the updated total.
DiscoveredRecent measurements
| Checked | Result | Response | Tools |
|---|---|---|---|
| Connected | 1607 ms | 320 |