MCP tool reference
Every tool JomForm exposes to connected AI agents via the Model Context Protocol. Connect any MCP client to https://mcp.jomform.com/mcp and agents discover these tools automatically. This page lists the whole registry — the api/internal/mcpsrv package at this release — and a guard fails the build if the two drift apart in either direction.
181 tools across 32 categories.
OAuth discovery
The connector authenticates with OAuth 2.1 (authorization code + PKCE, no API keys). A client discovers how to authenticate from the server itself:
- An unauthenticated request to
https://mcp.jomform.com/mcpis refused with401and aWWW-Authenticate: Bearer resource_metadata="…"header naming the protected-resource metadata URL. - The client fetches
/.well-known/oauth-protected-resource(RFC 9728). It names the protected resource (https://mcp.jomform.com/mcp), the authorization server (https://mcp.jomform.com), the scopes (readandreadwrite) and that the bearer token is presented in theAuthorizationheader. - It then fetches the authorization-server metadata at
/.well-known/oauth-authorization-server(RFC 8414) for the authorize, token, revoke and registration endpoints. - It registers itself dynamically, opens the authorize URL in the browser (you log in and grant access), and exchanges the code for an access token — sending the
resourceparameter on both the authorize and token requests. JomForm validates it: a value that is not this MCP endpoint is rejected and no token is issued. - Every later call to /mcp carries the access token as a Bearer credential.
Structured output
Tool results are returned as structured content, and every tool declares the shape of that result in its outputSchema. All tools declare an object schema, which is what makes the server usable with a client that validates tools/list strictly.
Every listing tool therefore wraps its array in an object: the records are under items, and paginated listings also carry page, limit and total. So list_products returns {"items": [...]}, not a bare array.
Batch tools report their summary next to the per-item outcomes: capture_all_pending_sales returns {"requested", "captured", "failed", "results": [...]}. Single-record tools return their fields at the top level of the object.
OAuth connector scopes
A connector authenticates with OAuth 2.1, and the grant it receives carries one of two scopes. The MCP client asks for the scope it wants on the authorize request (?scope=…, space-delimited); JomForm grants exactly what was asked for and records it on the token it issues. Both are advertised in the protected-resource and authorization-server metadata, alongside the other fields a client needs.
read— read-only. The agent can call every listing and getter; every tool that creates, updates, deletes, archives, cancels, refunds or generates a stored key is refused with a scope error. A read grant also cannot upgrade itself:create_api_keyis a write, so a read-only connector can never mint a wider credential.readwrite— read and write. This is the grant for a client that does not ask for a scope: the request omittingscopedefaults to readwrite, so an already-connected client is never silently downgraded.
The scope is fixed for the life of the connection: refreshing an access token returns the same grant as the token it replaces, so a read grant never widens. A request naming a scope this server does not advertise is refused with invalid_scope — it is never silently widened to readwrite or narrowed to read. The token response echoes the issued grant, not the advertised list, so a client can read back what it actually holds.
API-key scopes
An API key carries a scope, which says what that credential may do at all. It is one of:
readonly— read tools only. Every read works; every tool that creates, updates, deletes, archives, cancels, revokes or generates a stored key is refused with a scope error. A readonly key cannot mint another key, so it cannot escalate itself to readwrite.readwrite— read and write. This is the default grant for an OAuth connector, unless it asks forread.admin— kept for existing keys; it satisfies every scope, so it behaves as readwrite.
Scope is a separate axis from your workspace role (read_only, manage, admin). A write has to satisfy both: a readwrite key held by a read_only member is still refused, and so is a readonly key held by the owner. Reading never requires the write scope.
A few tools are worth calling out because their name suggests otherwise: get_webhook_signing_key needs readwrite only when it has to create the account's keypair on first use — reading an existing key does not.
Forms
list_formsSenarai borang akaun anda: id, slug, tajuk, status (draft|private|published|unlisted), mode (payment|response) dan URL awam bila ia live. Read-only — guna untuk penemuan id borang sebelum membaca atau menulis.
get_formBaca SATU borang sepenuhnya (read-only): id, slug, tajuk, status, mode, config — TERMASUK blocks semasa — theme, registered_fields, URL awam, created_at dan updated_at. Baca borang dahulu, ubah hanya medan yang dikehendaki, kemudian hantar semula config penuh melalui upsert_form. Menulis tanpa membaca dahulu boleh memusnahkan blok sedia ada.
upsert_formCipta/kemaskini borang (form_id kosong = baru). Baca sedia ada dengan get_form dahulu dan hantar config penuh supaya blok sedia ada tidak hilang. Theme (pilihan) = gaya/branding tersuai. slug (pilihan) = laluan URL borang: pada cipta ia digunakan bila diisi; pada kemaskini ia MENAMAKAN SEMULA borang (a-z, 0-9, sengkang; 1-40 aksara), URL lama kekal berfungsi melalui ubah hala sementara (301), dan slug yang sudah dipakai borang lain dalam workspace ini ditolak dengan ralat. Kosongkan slug untuk kekalkan yang sedia ada.
set_form_themeSet gaya/branding tersuai borang (issue #254): primary, background, text, card_background, card_border_radius, font_family, logo_url (mesti https:// — issue #501), layout (centered|full_width|split), spacing (compact|comfortable|spacious), product_image_size (small|medium|large|hidden, issue #471), product_image_layout (thumbnail-left|image-top — kad menu), custom_css, custom_js (issue #388 — JS tersuai untuk tingkah laku borang; dikawal pemilik + diimbas). Agent bebas set CSS/JS; JomForm kekal kuasa ke atas harga & pembayaran.
register_form_fieldsDaftar medan input borang (issue #254) dengan kontrak ketat: {field_id, type, label, required, validation?, product_id?}. type mesti jenis input JomForm (name|email|phone|textarea|number|select|radio|checkbox|date|time|website|address|consent|hidden|list|repeater|image-choice|captcha). Ganti semua medan sedia ada. Harga kekal dari DB produk (server authority).
generate_formJana konfigurasi borang (blok + gaya) dari prompt semula jadi (issue #256) via deepseek-v4.1-flash. Output disahkan server-side terhadap jenis blok JomForm; harga produk kekal dari DB (server authority). Semak/edit dulu, kemudian simpan guna upsert_form.
publish_formPublish borang — live di <workspace>.jomform.com/<slug> atau custom domain. status=private (dengan password) menjadikannya borang ber-kata laluan; status=draft menarik balik. Borang bayaran tanpa produk ditolak SEKALI dengan mesej jelas (ia tidak boleh menerima bayaran) — hantar acknowledge=true untuk teruskan. Borang response dan borang yang sudah live tidak pernah ditanya.
unpublish_formTarik balik borang dari live (jadikan draft).
set_form_passwordSet/buang kata laluan akses borang private (argon2id-hashed; tidak pernah dipulangkan). Password kosong = buang kata laluan.
get_form_restrictionsBaca sekatan borang (issue #207): jadual mula/tamat (start_at/end_at) + had penyertaan (entry_limit) + mesej peringkat.
set_form_restrictionsSet sekatan borang (issue #207): jadual mula/tamat + had penyertaan + mesej. Kosong = buang sekatan.
get_form_og_imageBaca imej Open Graph (og:image) borang (issue #411): URL imej tersuai bila ditetapkan, kosong = guna lalai janaan /og/{slug}.png.
set_form_og_imageSet imej Open Graph (og:image) borang (issue #411): URL imej tersuai (mesti https://) untuk pratonton sosial bila borang dikongsi. Kosong = guna lalai janaan.
delete_formSoft-delete borang (pergi ke trash dashboard; slug dilepaskan). Boleh dipulihkan — guna restore_form.
list_deleted_formsSenarai borang dalam TONG SAMPAH (deleted_at IS NOT NULL): id, slug, title, deleted_at, sales_count dan can_purge. Borang dengan jualan tidak boleh dipadam kekal (can_purge=false).
restore_formPulihkan borang dari tong sampah (deleted_at=NULL). Tiada apa-apa dimusnahkan — borang kembali dengan slug asalnya. Jika slug sudah diambil borang lain, pulihan DITOLAK (tiada penamaan semula automatik) — tukar slug borang itu dahulu.
purge_formPadam borang secara KEKAL dari tong sampah (submissions/notifikasi/confirmations/tetapan emel borang/webhook borang gugur bersama). AMARAN: tidak boleh dibatalkan. Wajib confirm_slug = slug borang itu. Borang yang ADA JUALAN ditolak — sales.form_id ialah ON DELETE SET NULL, jadi padam kekal akan menjadikan jualan yatim secara senyap. Hanya borang dalam tong sampah — padam dulu guna delete_form.
bulk_formsTindakan pukal atas banyak borang sekali gus: action=publish|draft|delete dengan ids[] (maks 500). Borang bayaran tanpa produk DIKECUALIKAN dari publish dan dipulangkan dalam needs_products — hantar acknowledge=true untuk menerbitkannya juga.
Products
list_productsSenarai semua produk aktif dalam akaun JomForm anda.
create_productCipta produk baharu. Untuk langganan, set type='subscription' dan billing_period='month'|'year'.
archive_productNyahaktifkan produk (dulang tanpa padam data).
list_product_variationsSenarai variasi produk (name, price_sen, stock, images, sort_order, dan kitaran langganan sendiri bila ada) untuk satu produk. Produk simple = senarai kosong. billing_period/trial_days/subscription_charges HANYA hadir bila variasi menetapkannya sendiri; jika tiada, variasi itu mewarisi kitaran produk.
create_product_variationTambah SATU variasi pada produk (cth: Saiz M), dengan harga, stok, gambar dan kitaran langganannya sendiri (billing_period month|year, trial_days, subscription_charges). Biarkan kosong = variasi mewarisi kitaran produk.
update_product_variationKemaskini SATU variasi (nama, harga, stok, gambar, kitaran langganan). Medan yang tidak diisi kekal. Set clear_billing_period / clear_trial_days / clear_subscription_charges = true untuk BUANG kitaran variasi supaya ia mewarisi produk semula.
delete_product_variationPadam SATU variasi dari produk. Variasi terakhir yang dipadam menjadikan produk itu produk simple semula.
update_productKemaskini produk: nama, harga (price_sen), stok, jenis (type), billing_period (langganan), aktif/tidak, images (URL imej mesti https:// — issue #501; omit = tak berubah). Omit medan tak berubah.
bulk_productsArkibkan banyak produk sekali gus (active=false). ids[] maks 500.
delete_productHantar produk ke TONG SAMPAH (deleted_at diisi) — ia keluar dari senarai produk dan boleh dipulihkan guna restore_product. Ini berbeza daripada archive_product (active=false), yang kekal dalam senarai. Untuk nyahaktifkan sahaja, guna archive_product.
list_deleted_productsSenarai produk dalam TONG SAMPAH: id, name, price_sen, deleted_at, sales_count, subscription_count dan can_purge. can_purge=false bermakna produk itu masih dirujuk jualan (sejarah) atau subscription (bil masih berjalan) — padam kekal akan DITOLAK.
restore_productPulihkan produk dari tong sampah (deleted_at=NULL). Tiada apa-apa dimusnahkan dan tiada nama yang perlu diselesaikan — produk tiada slug — jadi pulihan tidak boleh berlanggar.
purge_productPadam produk secara KEKAL dari tong sampah (variasinya gugur bersama melalui CASCADE). AMARAN: tidak boleh dibatalkan. Wajib confirm_name = nama produk itu. DITOLAK bila produk ada jualan (sejarah jualan tidak dimusnahkan) atau masih ada subscription (FK RESTRICT). Hanya produk dalam tong sampah — hantar ke bin dulu guna delete_product.
Sales
list_salesSenarai rekod terkini (terbaru dahulu). Tanpa form_id ini ialah SELURUH set rekod akaun: jualan DAN respons borang mode 'response' (sale_type 'response', submission_id + data) — sama seperti halaman Sales, bukan senarai kosong atau separa. Dengan form_id, mode borang itu menentukan: borang bayaran → jualan, borang respons → respons. Setiap jualan: refunded_sen (telah direfund), outstanding_sen (baki boleh refund), refund_status (none|partial|full) — issue #518. Status ialah soalan bayaran, jadi menetapkan status mengecualikan bahagian respons.
get_sales_summaryRingkasan jualan: bilangan mengikut status (termasuk refunded), refunded_sen (jumlah direfund) dan revenue_sen (nilai bersih selepas refund, issue #518).
confirm_saleSahkan jualan (contoh: selepas semak bukti bank transfer).
get_sale_email_statusStatus emel resit untuk satu jualan: Sent → Delivered → Opened → (Clicked) dengan masa. Guna ref_code dari list_sales.
export_sales_csvEksport set rekod akaun sebagai CSV. Tanpa form_id ia mengandungi jualan DAN respons (satu lajur record_type menandakan setiap baris, serta lajur jawapan yang direkodkan), sepadan dengan senarai yang dipaparkan. Boleh tapis form_id/status/date_from/date_to/q — penapis yang sama seperti senarai, supaya fail dan halaman sepadan. Kolum refund mendedahkan refund sebahagian (issue #518). Pulangkan kandungan csv.
Skip-capture
capture_saleCapture authorisasi skip_capture (kad buyer dicaj sekarang). amount_sen optional untuk partial.
release_saleRelease (void) authorisasi skip_capture — kad buyer TIDAK dicaj.
refund_saleRefund jualan yang telah dibayar (paid/confirmed). amount_sen = jumlah untuk refund separa; kosong = refund penuh baki yang tinggal. Jumlah mesti > 0 dan refunded_sen + amount_sen <= total_sen — refund melebihi baki ditolak sebelum CHIP dipanggil (issue #518). Refund separa MEREKOD jumlahnya dan jualan kekal 'paid'/'confirmed' dengan refund_status='partial'; hanya apabila baki penuh direfund barulah status jadi 'refunded'. Balasan: refunded_sen, outstanding_sen, refund_status, fully_refunded.
capture_all_pending_salesCapture SEMUA authorisasi skip_capture yang masih menunggu. Satu kegagalan tidak hentikan yang lain; balas per-ref.
Subscriptions
list_subscriptionsSenarai langganan akaun: pelanggan (emel), produk, jumlah/period, status (active/past_due/cancelled), tarikh caj seterusnya. Tapis ?status, page/limit.
cancel_subscriptionBatalkan langganan pelanggan. Kad token kekal pada merchant — boleh aktif semula.
retry_subscriptionCuba semula langganan yang past_due (round 13c): kembalikan status kepada active dan tetapkan ia due supaya sweep seterusnya mencaj kad semula. Guna selepas pelanggan top-up atau betulkan kad. Hanya past_due layak; cancelled/completed ditolak. Tidak mencaj serta-merta.
update_subscription_cardKemas kini kad pada langganan sedia ada (issue #154): cipta token purchase baharu supaya pelanggan boleh sahkan kad baharu. Pulangkan checkout_url untuk dihantar kepada pelanggan. Token lama kekal di CHIP (tidak dibatalkan) tetapi tidak lagi digunakan.
CHIP per-form
set_form_chip_overrideSet kredensial CHIP khusus untuk SATU borang (brand_id + secret_key). Bila set, borang tersebut guna kredensial ini, bukan default akaun. Secret disimpan terenkripsi.
clear_form_chip_overrideKosongkan override sebuah borang — kembali guna kredensial default akaun.
get_form_chip_feedsBaca senarai feed CHIP borang (issue #208). Setiap feed ada brand_id + secret (tersembunyi) + logik bersyarat. Feed tanpa condition = lalai (sentiasa padan).
set_form_chip_feedsSet senarai feed CHIP borang (issue #208). Feed pertama yang padan (ikut nilai medan borang) dipakai untuk pembayaran; feed tanpa condition = lalai. Secret kosong pada feed sedia ada = kekalkan kunci lama.
get_form_chip_mappingBaca peta medan CHIP borang (medan CHIP → id blok borang). Kosong = guna lalai (name→full_name, email→email, phone→phone).
set_form_chip_mappingSet peta medan CHIP borang (medan CHIP → id blok borang), cth {"full_name":"block_abc","email":"block_def"}. Mapping kosong = guna lalai.
CHIP account
get_chip_settingsBaca status kredensial CHIP akaun: configured, verified, mode, brand_id, secret_key_masked, email_fallback, whitelist. Secret penuh tidak pernah dipulangkan. Read-only.
set_chip_settingsSet kredensial CHIP akaun (brand_id + secret_key). Secret kosong = kekalkan kunci sedia ada. Disahkan terhadap CHIP sebelum disimpan. Admin sahaja.
clear_chip_settingsKosongkan kredensial CHIP akaun. Admin sahaja.
Form email & notifications
get_form_email_settingsBaca tetapan emel SATU borang (issue #453): override per-borang (subjek/badan resit, hantar resit, notifikasi merchant, HTML), tetapan workspace yang diwarisi, dan nilai efektif (resolusi per-borang -> workspace -> lalai).
set_form_email_settingsSet override emel SATU borang (issue #453): receipt_subject, receipt_body, receipt_enabled_override, notify_merchant_override, merchant_notify_email, html_override. Toggle tri-state — true/false = override, null = ikut workspace. Omit medan = kekal.
clear_form_email_overridesPadam SEMUA override emel per-borang supaya borang kembali mewarisi tetapan emel workspace (issue #453).
get_form_notificationsBaca senarai peraturan notifikasi emel per-borang (issue #193): event, penerima, subjek/badan, logik bersyarat, aktif. Setiap borang baharu disertakan satu notifikasi lalai kepada merchant: event sale.paid untuk borang pembayaran, response.submitted untuk borang respons (issue #597).
set_form_notificationsGanti SEMUA peraturan notifikasi emel per-borang (issue #193). notifications[] penuh: {id?, name, event, to_email, subject, body, condition?, enabled?}. event: sale.paid|sale.failed|response.submitted|subscription.charged. to_email: emel, {admin_email}, atau {field_id}. condition: {field_id, op, value}|null (op: equals|not_equals|contains|gt|lt|not_empty). Senarai kosong = tiada emel dihantar.
get_form_confirmationsBaca pengesahan borang (issue #179): senarai confirmation (message/redirect) + logik bersyarat. Pengesahan lalai sentiasa wujud.
set_form_confirmationsSet pengesahan borang (issue #179): ganti semua confirmation (name, type message|redirect, message/redirect_url, condition). Pengesahan lalai sentiasa dikekalkan. Mesej HTML disanitasi (tiada XSS).
get_form_failure_messageBaca mesej pembayaran-gagal borang (issue #393): apa yang pembeli lihat bila pembayaran gagal. Kosong = guna mesej lalai.
set_form_failure_messageSet mesej pembayaran-gagal borang (issue #393): HTML yang pembeli lihat bila pembayaran gagal (disanitasi, tiada XSS). Kosong = guna mesej lalai.
Email settings
get_email_settingsBaca tetapan emel satu workspace: subjek/badan resit (placeholder), hantar resit, notifikasi merchant, HTML.
set_email_settingsSet tetapan emel workspace: receipt_subject, receipt_body ({{ref_code}}, {{amount}}, {{items}}, {{form_url}}, {{buyer_name}}, {{date}}), receipt_enabled, notify_merchant, merchant_notify_email, html. Omit medan = kekal.
e-Invoice
set_einvoiceSet TIN + MSIC LHDN untuk resit e-Invoice (B2B).
get_einvoice_settingsBaca TIN + MSIC LHDN semasa untuk resit e-Invoice.
Webhooks
create_webhookDaftar endpoint webhook (events: sale.paid|sale.failed|subscription.charged|*). HMAC-signed. form_id pilihan: set = webhook hanya untuk borang itu; kosong = seluruh workspace/akaun.
list_webhooksSenarai outbound webhooks akaun. form_id pilihan: set = webhook borang itu sahaja; kosong = webhook seluruh workspace/akaun.
delete_webhookPadam satu webhook (id dari list_webhooks).
update_webhookKemas kini webhook (url, events, secret pilihan — kosong = kekal sedia ada). events: sale.paid|sale.failed|subscription.charged|*.
webhook_deliveriesLog penghantaran terkini webhook (status_code, attempt, error).
retry_webhook_deliveryCuba semula satu penghantaran webhook yang gagal dengan segera (id dari webhook_deliveries). Ia dienqueue semula dan dihantar semula pada sweep seterusnya.
get_webhook_signing_keyBaca kunci tandatangan Ed25519 akaun (key_id, public_key, alg) untuk mengesahkan tandatangan webhook X-JomForm-Signature-Ed25519. Membaca kunci sedia ada tidak memerlukan skop tulis; JANA kunci baharu (kali pertama, atau selepas dipadam) memerlukan readwrite.
API keys
list_api_keysSenarai semua API key akaun (id, nama, scopes, created_at, last_used). Key material tidak pernah dikembalikan.
create_api_keyCipta API key baharu (nama, scopes? readonly|readwrite, default readonly). Key penuh dipapar SEKALI sahaja — simpan segera; store simpan hash sahaja. SCOPES: baca untuk readonly, baca+tulis untuk readwrite.
revoke_api_keyPadam API key secara PERMANEN (id dari list_api_keys; tiada disable/enable semula — cipta key baru). AMARAN: jika id ialah key yang sedang mengesahkan sesi ini, ia akan memutuskan akses sendiri serta-merta — set confirm=true untuk sahkan; sebaliknya panggilan ditolak.
Affiliates
create_affiliateCipta affiliate untuk workspace: kod rujukan (?aff=CODE), nama, emel, kadar komisen. Jualan yang dibawa affiliate dikreditkan kepadanya.
list_affiliatesSenarai semua affiliate dalam satu workspace — id, kod, nama, komisen, URL rujukan.
get_affiliate_salesSenarai jualan yang dibawa oleh satu affiliate (read-only, skop workspace).
set_affiliate_commissionSet kadar komisen affiliate (cth: 10 = 10%).
Workspaces & storefronts
list_workspacesSenarai semua workspace (storefront) anda — id, nama, slug dan URL.
create_workspaceCipta workspace baharu. Nama sehingga 120 aksara (dikira sebagai AKSARA, bukan bait). Slug 3-40 aksara (a-z, 0-9, dash); URL jadi https://<slug>.jomform.com. Nama yang dikhaskan untuk JomForm (cth: www, api, app, mail, s3, docs, mcp, templates, custom, support, help, status, billing, admin, account, login, payments, checkout, cdn, assets) ditolak — nama itu milik JomForm atau melindungi pengguna daripada penipuan (cth: billing.jomform.com yang menyamar sebagai saluran rasmi JomForm).
update_workspaceTukar NAMA dan/atau SLUG (alamat awam) satu workspace. Medan kosong = kekal. Nama sehingga 120 AKSARA (bukan bait; sama had dengan cipta). Menukar nama perlukan peranan manage/admin; menukar SLUG hanya PEMILIK dan perlukan confirm=true — slug ialah hos awam tenant (https://<slug>.jomform.com) yang sudah tercetak dalam kod QR, iframe dan pautan affiliate. Slug LAMA disimpan sebagai alias dan TERUS BERFUNGSI (pindah kekal ke slug baharu), jadi pautan yang sudah tersebar tidak mati; jawapan membawa previous_url + previous_url_redirects. Nama yang dikhaskan untuk JomForm (senarai sama seperti create_workspace: www, api, billing, support, docs, mcp, templates, dll.) DITOLAK sebagai status=refused dengan code reserved_subdomain — nama itu milik JomForm atau melindungi pengguna daripada penipuan, dan slug workspace tidak berubah. REST: PATCH /v1/workspaces/{id}.
get_workspace_settingsBaca tetapan storefront satu workspace: footer, company_reg, privacy/terms/refund URL, contact, menu, indexing, sound_enabled, spam_judge_enabled (AI spam detection, this change — true = hidup/lalai).
set_workspace_settingsSet tetapan storefront workspace: footer_text, company_reg, privacy_url/terms_url/refund_url (mesti https://), contact_phone, contact_address, business_name, menu_json, default_lang (bahasa lalai borang AWAM untuk workspace ini — en, ms, zh, ta atau ar; ini bahasa pembeli, bukan bahasa dashboard anda), indexing, sound_enabled, spam_judge_enabled (this change: false = MATIKAN AI spam detection untuk workspace ini — tiada job AI dihantar langsung dan is_spam tidak lagi ditetapkan, jadi /spam kekal kosong). Omit medan = kekal.
get_workspace_landing_pageBaca konfigurasi 'landing page' workspace (issue #360): hero, subtitle, body, image_url, theme colors, logo, CTA (label + form_slug). Kosong jika tiada landing page ditetapkan.
set_workspace_landing_pageSet 'landing page' storefront workspace (issue #360): enabled (true = tunjuk, false/kosong = jatuh ke senarai borang), hero, subtitle, body, image_url (mesti https:// — issue #501), primary_color, background_color, logo_url (mesti https:// — issue #501), ctas ([{label, form_slug}]). Hantar enabled:false untuk pulang ke senarai borang.
generate_qrJana QR code untuk borang diterbitkan (issue #438): form_slug + table pilihan (nombor meja, dikodkan sebagai ?table=N). Pulangkan qr_url, data_url (PNG base64) dan target_url. Guna untuk stand QR di meja restoran.
get_menu_pageBaca konfigurasi menu restoran satu BORANG (issue #497): form_id. Pulangkan enabled, categories ([{name, product_ids}]), best_seller, best_seller_ids (senarai, #485), order_form_slug (borang maklumat tambahan pesanan, #491/#493). Menu ialah tetapan PER-BORANG dan dipapar di URL borang itu sendiri. Kosong jika borang tiada menu.
set_menu_pageSet menu restoran untuk satu BORANG (issue #497): form_id, enabled (true = borang ini jadi menu di URL-nya; false = borang kembali normal), categories ([{name, product_ids}]), best_seller (ID produk) atau best_seller_ids (senarai ID untuk seksyen Best Seller, #485 — mengatasi best_seller), order_form_slug (pilihan, #491/#493 — borang diterbitkan dalam akaun sama yang medan bloknya menjadi medan tambahan semasa checkout menu; medan dirender dengan label/required borang itu, disahkan server-side, dan disimpan pada jualan; kosong = checkout menu nama + emel sahaja). Produk menu datang dari blok produk BORANG itu sendiri (bukan semua produk akaun), jadi produk akaun lain tidak boleh bocor masuk; kategori/best-seller mesti merujuk produk yang dirujuk borang. Halaman menu menunjukkan nombor meja dari ?table=N, ada penapis kategori + carian, dan checkout terus dari halaman menu (nama + emel pembeli wajib untuk CHIP — tidak boleh dibuang walaupun borang tambahan tidak menyenaraikannya).
get_currency_ratesKadar tukaran wang BNM (Ringgit per unit mata wang asing), dicache harian. Guna untuk paparan/penukaran amaun baharu.
set_workspace_currencySet mata wang workspace (default MYR). Perubahan terpakai pada produk/jualan baharu; jualan lama kekal mata wang asal (tidak ditukar balik).
check_workspace_deletionSemak sama ada satu workspace boleh dihantar ke tong sampah (read-only, tiada kesan). Pulangkan can_delete, empty, last_workspace, is_owner dan blockers[] (setiap kategori + kiraan). can_delete = pemilik DAN bukan workspace terakhir — kandungan TIDAK lagi menyekat. Guna ini dahulu supaya anda boleh menyatakan sebabnya, bukan gagal selepas menekan.
delete_workspaceHantar workspace ke TONG SAMPAH (pemilik sahaja — ahli admin tidak boleh; mereka keluar melalui remove_workspace_member). Tiada apa-apa dimusnahkan: borang, produk, jualan, akaun CHIP dan kunci API semuanya kekal, dan restore_workspace membawanya kembali. DUA syarat: (1) confirm_slug wajib sepadan dengan slug workspace; (2) anda mesti ada sekurang-kurangnya satu workspace LAIN — workspace terakhir tidak boleh dibuang, kerana satu pengguna wajib ada satu workspace. Workspace yang PENUH boleh masuk tong sampah. Padam KEKAL hanya melalui purge_workspace, dan chat tidak pernah boleh mencadangkan alat itu — ia alat MCP untuk pemilik, bukan untuk chat.
list_trashed_workspacesSenarai workspace dalam TONG SAMPAH anda (deleted_at IS NOT NULL): id, slug, name, deleted_at. Milik sendiri sahaja — hanya pemilik boleh pulih atau padam kekal.
restore_workspacePulihkan workspace dari tong sampah (deleted_at=NULL). Tiada apa-apa dimusnahkan — workspace kembali dengan akaun, borang dan jualannya. Jika slug asalnya sudah diambil workspace lain semasa ia dalam tong sampah, pulihan DITOLAK (tiada penamaan semula automatik). Pemilik sahaja.
purge_workspacePadam workspace secara KEKAL dari tong sampah (akaun CHIP, borang, produk, kunci API dan jualannya gugur bersama). AMARAN: tidak boleh dibatalkan. Wajib confirm_slug = slug workspace itu. Hanya workspace DALAM TONG SAMPAH: hantar ke tong sampah dahulu guna delete_workspace. Workspace yang ADA JUALAN ditolak. TIDAK PERNAH DICADANGKAN OLEH CHAT: chat dalam aplikasi tidak boleh mencadangkan alat ini; ia alat MCP untuk pemilik (sama kelas dengan purge_form/purge_product/purge_media).
Workspace members
invite_workspace_memberJemput ahli ke workspace (admin sahaja). Jika emel sudah berdaftar, tambah terus sebagai ahli; jika tidak, cipta jemputan menunggu. Role: read_only|manage|admin.
list_workspace_membersSenarai ahli workspace + peranan (admin sahaja).
update_workspace_member_roleTukar peranan ahli workspace (admin sahaja). Role: read_only|manage|admin.
remove_workspace_memberBuang ahli dari workspace (admin sahaja). Pemilik tidak boleh dibuang.
list_workspace_invitesSenarai jemputan menunggu untuk workspace (admin sahaja).
cancel_workspace_inviteBatal jemputan menunggu untuk workspace (admin sahaja).
set_default_workspaceSet workspace lalai anda (workspace yang anda masuk terus selepas log masuk, tanpa prompt). workspace_id kosong = buang default. Workspace mesti milik anda.
Profile & security
update_profile_phoneSet nombor telefon pilihan pada profil anda (kosong = buang). Format longgar: digit, +, ruang, 8-15 digit.
update_profile_timezoneSet zona waktu IANA pilihan pada profil anda (kosong = Auto, ikut pelayar/PC). Contoh: Asia/Kuala_Lumpur, UTC.
update_profile_languageSet bahasa DASHBOARD pilihan pada profil anda (per-user, ikut anda ke mana-mana pelayar): en, ms, zh, ta atau ar (kosong = Auto, ikut nilai terakhir pelayar itu, kemudian en). Ini bahasa anda sendiri di dashboard, BUKAN bahasa pembeli pada borang awam — itu ialah default_lang workspace (set_workspace_settings) atau config.lang per borang.
update_profile_avatarSet avatar profil (URL mesti imej yang dihoskan JomForm dari upload bucket). Kosong = buang avatar.
list_passkeysSenarai passkey (WebAuthn) akaun anda: id. Read-only.
delete_passkeyPadam satu passkey (id dari list_passkeys).
list_social_accountsSenarai akaun sosial yang dipautkan (google|azure_ad|github|cloudflare): provider, emel, nama, picture, linked_at. Read-only.
unlink_social_accountNyah-pautkan akaun sosial (provider dari list_social_accounts).
get_2fa_statusBaca status 2FA akaun anda: enabled (true/false). Read-only.
setup_2faJana rahsia TOTP untuk 2FA (belum aktif). Pulangkan secret + otpauth_url untuk diimbas aplikasi authenticator, kemudian panggil enable_2fa dengan kod.
enable_2faAktifkan 2FA dengan kod 6-digit dari aplikasi authenticator (selepas setup_2fa).
disable_2faMatikan 2FA akaun anda. TEPAT SATU bukti diperlukan, bukan kedua-duanya: kod 2FA semasa (totp_code — kod 6 digit dari aplikasi authenticator, disahkan sekali sahaja dan tidak memerlukan kata laluan) ATAU kata laluan akaun (password). Menghantar kedua-duanya ditolak oleh skema, begitu juga tanpa kedua-duanya, dan kunci API sahaja tidak pernah memadai. Borang di dashboard (Profil → Keselamatan) meminta kod 2FA semasa. Mematikan 2FA juga membatalkan SEMUA peranti dipercayai akaun — peranti yang melangkau faktor yang kini dibuang tidak boleh kekal dikecualikan, sama seperti borang profil dashboard; guna list_trusted_devices untuk semak baki peranti.
list_trusted_devicesSenarai peranti dipercayai 2FA akaun anda (label, ip, first_seen, expires_at). Peranti dipercayai melangkau kod 2FA selama 30 hari. Read-only.
revoke_trusted_deviceBatalkan satu peranti dipercayai (device_id dari list_trusted_devices). Log masuk seterusnya pada peranti itu akan meminta kod 2FA semula — serta-merta.
revoke_all_trusted_devicesBatalkan SEMUA peranti dipercayai akaun anda ("log keluar di semua tempat"). Setiap peranti akan meminta kod 2FA semula.
Feedback
list_feedbackSenarai thread maklum balas anda sendiri (terbaru dahulu), boleh tapis status (open|in_progress|resolved|closed) dan berhalaman (page/limit). Skop ialah PENGGUNA yang disahkan, bukan workspace — maklum balas bukan data akaun. Super-admin melihat SEMUA thread (triage). Read-only.
get_feedback_threadBaca satu thread maklum balas beserta semua mesejnya (author_kind: user|admin), terawal dahulu. Thread milik pengguna lain menjawab 'tidak dijumpai' — sama seperti id yang tidak wujud. Super-admin boleh membaca mana-mana thread. Read-only.
reply_feedbackBalas dalam thread maklum balas. Penulis ditentukan oleh pemanggil: super-admin membalas sebagai admin (dan thread 'open' bergerak ke in_progress), pengguna lain membalas sebagai pemilik thread. Body minimum 10 aksara. Skop pengguna — tidak boleh membalas thread orang lain.
set_feedback_statusTukar status thread maklum balas (open|in_progress|resolved|closed). Keputusan triage — super-admin sahaja; diaudit. Status adalah per-thread, bukan per-mesej.
retry_feedback_notifyHantar semula notifikasi emel yang GAGAL untuk satu thread maklum balas (medan notify_failed_at pada thread). Ini laluan pemulihan untuk kegagalan penghantaran: emel yang tidak sempat masuk baris gilir ditandakan pada thread supaya kelihatan, dan tool ini cuba semula melalui saluran yang sama. Keputusan: queued|sent|failed|none ('none' bermaksud tiada kegagalan direkodkan). Super-admin sahaja; diaudit.
submit_feedbackFailkan maklum balas atau permintaan ciri baharu daripada perbualan chat. Sama seperti menghantar di halaman /feedback: disimpan sebagai thread yang boleh anda baca dan balas, dan pasukan JomForm dimaklumkan. Body minimum 10 aksara, maksimum 5000; maksimum 10 penghantaran sehari bagi setiap pengguna. Memerlukan token PENGGUNA (OAuth/sesi) dan skop readwrite — API key ditolak kerana maklum balas ialah data peribadi pengguna.
Keselamatan sesi
Sesi log masuk melalui pautan emel (magic link) ditandakan magic. Jika 2FA aktif, tindakan sensitif (tukar kata laluan, nyahaktif 2FA, tambah passkey) memerlukan kod 2FA baharu. Pengguna tanpa 2FA atau sesi log masuk biasa tidak terjejas.
Legal pages
generate_legal_pageJana draf halaman undang-undang (privacy|terms|refund) untuk workspace melalui LLM (deepseek-v4.1-flash) dengan template yang diisi butiran perniagaan. Pulangkan draf untuk semakan — belum disimpan sehingga set_legal_page dipanggil.
get_legal_pageBaca satu halaman undang-undang workspace (privacy|terms|refund): mode (auto/manual) + kandungan.
set_legal_pageSimpan kandungan halaman undang-undang workspace (privacy|terms|refund) dengan mode auto|manual. Kandungan dipaparkan di {workspace}.jomform.com/privacy|terms|refund.
Event tracking
list_eventsLog audit/tingkah-laku akaun (terbaru dahulu). Boleh tapis event_name, from/to (RFC3339), dan page/limit. Read-only. Pada dashboard, log yang sama dipaparkan pada halaman Activity — dan workspace-scoped di sana melalui ?workspace=<slug>.
Email delivery
list_email_deliveriesStatus penghantaran emel akaun (terbaru dahulu): delivered/bounce/complaint/open/click dengan emel, masa, sale_ref dan detail. Boleh tapis form_id, sale_ref, event_type, dan page/limit. Read-only.
Form submissions
list_form_submissionsSenarai respons (submission) untuk satu borang respons-sahaja (mode 'response', tanpa bayaran). Pulangkan data medan + masa. Tapis form_id, page/limit.
Spam review
list_spam_submissionsSenarai submission yang ditanda spam oleh AI (issue #245): data medan, sebab spam, masa. Boleh tapis page/limit. Read-only.
unspam_submissionNyah-tanda spam pada submission (false positive). Write tool (peranan manage/admin).
list_spam_salesSenarai jualan yang ditanda spam oleh AI (issue #245): ref, status, jumlah, pembeli, sebab spam, masa. Boleh tapis page/limit. Read-only.
unspam_saleNyah-tanda spam pada jualan (false positive). Write tool (peranan manage/admin).
Media
list_mediaSenarai media yang dimuat naik akaun (perpustakaan media): thumbnail/url, nama, tarikh, saiz. Boleh tapis form_id dan source (merchant|customer). Read-only.
upload_mediaMuat naik media/aset ke workspace anda (issue #363): kind='image' (png/jpeg/webp/gif — dioptimumkan, strip EXIF, terus boleh guna; png/jpeg/webp dapat saiz thumbnail/medium/full), kind='asset' (css/js/html/svg/woff2 — disimpan asli, dikaji sebelum dipapar) atau kind='pdf' (disimpan asli, terus boleh guna). hantar content fail sebagai base64 di 'data'. Pulangkan URL untuk diguna sebagai image_url/logo_url landing atau <link>/<script> landing page.
delete_mediaHantar media ke TONG SAMPAH (deleted_at diisi). Byte TIDAK dimusnahkan dan kuota TIDAK dilepaskan — objek masih di storan, jadi ia masih dikira terhadap had 1GB. URL yang sama terus berfungsi selepas restore_media. Guna purge_media untuk memusnahkan byte secara kekal.
list_deleted_mediaSenarai media dalam TONG SAMPAH: id, key, url, mime, size, form_id, deleted_at dan object_present. object_present=false bermakna byte sudah tiada dalam storan (dipadam operator atau lifecycle rule) — restore akan DITOLAK.
restore_mediaPulihkan media dari tong sampah (deleted_at=NULL). URL yang SAMA terus berfungsi. DITOLAK (409) bila objek sudah tiada dalam storan — memulihkan baris itu hanya akan memberi URL yang 404.
purge_mediaPadam media secara KEKAL dari tong sampah. AMARAN: tidak boleh dibatalkan. Inilah satu-satunya tempat byte dipadam dari storan DAN kuota 1GB dilepaskan (varians saiz + pratonton PDF turut dipadam). Hanya media dalam tong sampah — hantar ke bin dulu guna delete_media.
Templates
list_templatesSenarai template borang sistem (gallery): slug, tajuk, kategori, mode (payment/response). Guna dengan use_template untuk quickstart.
use_templateClone template borang menjadi draft baharu dalam akaun anda. Isi slug (dari list_templates) untuk template sistem, atau template_id (dari list_own_templates) untuk template anda sendiri. Pulangkan form_id + slug untuk dibuka dalam builder.
list_own_templatesSenarai template anda sendiri (issue #457): id, slug, tajuk, config, theme. Berbeza daripada list_templates (gallery sistem).
create_templateSimpan borang sedia ada sebagai template boleh guna semula (issue #457): isi form_id (dari list_forms) untuk salin borang itu, atau title + config untuk template baharu. Template dimiliki akaun anda sahaja.
update_templateKemaskini template anda sendiri (issue #457): tajuk, config dan/atau theme. Omit medan yang tak berubah. template_id dari list_own_templates.
delete_templatePadam (soft-delete) template anda sendiri (issue #457). template_id dari list_own_templates. Boleh dicipta semula bila-bila masa.
Analytics
get_form_analyticsAnalitik per-borang: bilangan entri (submissions), paid/confirmed/failed/pending, dan hasil (RM) — NET selepas refund. submissions = entri yang direkod untuk borang itu, dibaca dari jadual yang borang tulis: mode=response → jadual submissions; borang bayaran → sales (semua status). mode disertakan supaya label betul. Merentas semua workspace anda. Read-only.
get_product_analyticsAnalitik per-produk: unit terjual (paid), hasil (RM). Merentas semua workspace anda. Read-only.
Custom domains
list_domainsSenarai domain tersuai akaun: id, domain, provider, verified, auto_cname. Read-only.
add_domainTuntut domain tersuai baharu (cth: kedai.com). Kemudian set CNAME → custom.jomform.com dan panggil verify_domain.
verify_domainSahkan domain tersuai (id dari list_domains). Hanya disahkan bila domain benar-benar memaparkan halaman JomForm.
remove_domainBuang tuntutan domain tersuai (id dari list_domains).
auto_cname_domainCipta rekod CNAME Cloudflare secara automatik untuk domain (perlu Cloudflare dipautkan). Pulangkan auto_cname=created|failed.
get_domain_tls_emailBaca emel ACME/Let's Encrypt per-domain untuk auto-TLS. Kosong = kembali ke emel akaun, kemudian ACME_EMAIL global. Read-only.
set_domain_tls_emailTetapkan emel ACME/Let's Encrypt per-domain untuk auto-TLS. Kosong = kembali ke emel akaun, kemudian ACME_EMAIL global.
set_domain_tls_modeTetapkan mode TLS per-domain: auto (Let's Encrypt, lalai) atau custom (bawa sijil sendiri). Dalam mode custom, muat naik sijil dengan set_domain_custom_cert.
set_domain_custom_certMuat naik sijil TLS sendiri (bring-your-own-cert) untuk domain: cert + key dalam format PEM. Domain disajikan sijil ini.
Realtime notifications
get_realtime_statusBaca saluran notifikasi masa nyata satu workspace: URL stream SSE (GET /v1/rest/events/stream), peristiwa yang dihantar (sale.paid, sale.failed, response.submitted, subscription.charged, sale.refunded) dan togol bunyi. Read-only. Sambungkan ke stream untuk notifikasi segera pada skrin dapur/pesanan.
set_realtime_soundHidup/matikan bunyi beep notifikasi masa nyata satu workspace (true = bunyi bila jualan/respons baharu tiba; false = senyap).
Tracking
get_trackingBaca skrip penjejakan global akaun (Google Analytics, Facebook Pixel, dll) yang disuntik ke <head> borang awam. Read-only.
set_trackingSet skrip penjejakan global akaun. Hanya <script src=https://...>, <meta> dan komen dibenarkan (disanitasi).
System settings
get_system_settingsBaca semua tetapan sistem platform (site_*, landing_*). Read-only.
set_system_settingSet satu tetapan sistem (key mesti bermula site_ atau landing_).
Cloudflare
get_cloudflare_statusBaca status pautan Cloudflare akaun: linked, configured. Read-only.
connect_cloudflare_api_tokenPautkan Cloudflare via API token (disahkan serta-merta, disimpan terenkripsi).
connect_cloudflare_global_keyPautkan Cloudflare via global API key (disahkan serta-merta, disimpan terenkripsi).
disconnect_cloudflareNyah-pautkan Cloudflare dari akaun.
Account
get_accountMaklumat asas akaun (nama, slug, CHIP mode).
Admin (super-admin, platform-wide)
purge_unverified_accountsLaporkan bilangan akaun belum disahkan (emel) yang lebih lama daripada ambang, DIPECAH kepada berapa yang sweep akan padam dan berapa yang TERSEKAT (akaun masih memegang jualan atau langganan, jadi ia tidak akan dipadam). Admin sahaja; read-only — tidak memadam apa-apa.
admin_list_workspacesSenarai SEMUA workspace (storefront) platform: id, nama, slug, emel pemilik, tarikh cipta, bilangan borang + produk. Super-admin sahaja; read-only; diaudit.
admin_media_usagePenggunaan storan media per workspace (bait) berbanding kuota 1GB setiap workspace. Super-admin sahaja; read-only; diaudit.
admin_list_salesSenarai jualan merentas SEMUA workspace, boleh tapis (date_from/date_to, workspace_id, status). Super-admin sahaja; read-only; diaudit.
admin_spam_queueSenarai semua item spam (submission + jualan) merentas semua workspace. Super-admin sahaja; read-only; diaudit.
admin_email_deliveriesStatus penghantaran emel merentas SEMUA workspace (delivered/bounce/complaint/open/click), boleh tapis sale_ref/event_type/form_id. Super-admin sahaja; read-only; diaudit.
admin_dead_letter_eventsSenarai peristiwa callback masuk yang di-park sebagai dead letter (issue #574): dedup_key, event_type, bilangan cubaan, sebab dan masa. Selepas #569 peristiwa yang tak boleh diproses di-park 'dead' dan berhenti dicuba — sebelum ini ia tidak kelihatan langsung. Super-admin sahaja; read-only; diaudit. Di halaman /admin senarai yang sama dipaparkan dengan butang requeue setiap baris (GET /v1/admin/dead-letters, issue #579), supaya operator nampak callback yang gagal tanpa perlu klien MCP.
admin_requeue_dead_letter_eventHidupkan semula peristiwa dead letter supaya sweep replay mencubanya lagi (issue #574), selepas punca dibetulkan. Kiraan cubaan di-reset supaya ia tidak di-park semula oleh sweep yang sama. Super-admin sahaja; diaudit. Dari halaman /admin tindakan yang sama boleh dijalankan (POST /v1/admin/dead-letters/requeue, issue #579).
admin_system_healthKesihatan sistem: uptime, bilangan ralat MCP terkini (24j), kedalaman queue emel. Super-admin sahaja; read-only; diaudit.
admin_list_suppressionsSenarai alamat yang disekat daripada dihantar emel (issue #601): emel, sebab (hard_bounce|complaint|manual), sumber, nota, siapa tambah dan bila. Boleh cari (q) dan berhalaman (page/limit). Super-admin sahaja; read-only; diaudit. Di halaman /admin senarai yang sama dipaparkan dengan kotak carian dan pager (GET /v1/admin/suppressions, issue #601).
admin_add_suppressionTambah alamat ke senarai sekatan secara manual supaya tiada emel dihantar kepadanya (issue #601). reason: hard_bounce|complaint|manual (lalai manual), note pilihan. Idempotent. Super-admin sahaja; diaudit. Dari halaman /admin ia ditambah melalui borang manual (POST /v1/admin/suppressions, issue #601).
admin_remove_suppressionBuang alamat dari senarai sekatan supaya penghantaran emel bermula semula (issue #601) — untuk kes positif palsu. Super-admin sahaja; diaudit. Dari halaman /admin ia dibuang dengan butang + pengesahan (DELETE /v1/admin/suppressions/{email}, issue #601).