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:

  1. An unauthenticated request to https://mcp.jomform.com/mcp is refused with 401 and a WWW-Authenticate: Bearer resource_metadata="…" header naming the protected-resource metadata URL.
  2. 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 (read and readwrite) and that the bearer token is presented in the Authorization header.
  3. It then fetches the authorization-server metadata at /.well-known/oauth-authorization-server (RFC 8414) for the authorize, token, revoke and registration endpoints.
  4. 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 resource parameter 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.
  5. 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_key is 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 omitting scope defaults 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 for read.
  • 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_forms

Senarai 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_form

Baca 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_form

Cipta/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_theme

Set 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_fields

Daftar 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_form

Jana 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_form

Publish 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_form

Tarik balik borang dari live (jadikan draft).

set_form_password

Set/buang kata laluan akses borang private (argon2id-hashed; tidak pernah dipulangkan). Password kosong = buang kata laluan.

get_form_restrictions

Baca sekatan borang (issue #207): jadual mula/tamat (start_at/end_at) + had penyertaan (entry_limit) + mesej peringkat.

set_form_restrictions

Set sekatan borang (issue #207): jadual mula/tamat + had penyertaan + mesej. Kosong = buang sekatan.

get_form_og_image

Baca imej Open Graph (og:image) borang (issue #411): URL imej tersuai bila ditetapkan, kosong = guna lalai janaan /og/{slug}.png.

set_form_og_image

Set imej Open Graph (og:image) borang (issue #411): URL imej tersuai (mesti https://) untuk pratonton sosial bila borang dikongsi. Kosong = guna lalai janaan.

delete_form

Soft-delete borang (pergi ke trash dashboard; slug dilepaskan). Boleh dipulihkan — guna restore_form.

list_deleted_forms

Senarai 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_form

Pulihkan 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_form

Padam 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_forms

Tindakan 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_products

Senarai semua produk aktif dalam akaun JomForm anda.

create_product

Cipta produk baharu. Untuk langganan, set type='subscription' dan billing_period='month'|'year'.

archive_product

Nyahaktifkan produk (dulang tanpa padam data).

list_product_variations

Senarai 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_variation

Tambah 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_variation

Kemaskini 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_variation

Padam SATU variasi dari produk. Variasi terakhir yang dipadam menjadikan produk itu produk simple semula.

update_product

Kemaskini 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_products

Arkibkan banyak produk sekali gus (active=false). ids[] maks 500.

delete_product

Hantar 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_products

Senarai 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_product

Pulihkan 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_product

Padam 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_sales

Senarai 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_summary

Ringkasan jualan: bilangan mengikut status (termasuk refunded), refunded_sen (jumlah direfund) dan revenue_sen (nilai bersih selepas refund, issue #518).

confirm_sale

Sahkan jualan (contoh: selepas semak bukti bank transfer).

get_sale_email_status

Status emel resit untuk satu jualan: Sent → Delivered → Opened → (Clicked) dengan masa. Guna ref_code dari list_sales.

export_sales_csv

Eksport 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_sale

Capture authorisasi skip_capture (kad buyer dicaj sekarang). amount_sen optional untuk partial.

release_sale

Release (void) authorisasi skip_capture — kad buyer TIDAK dicaj.

refund_sale

Refund 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_sales

Capture SEMUA authorisasi skip_capture yang masih menunggu. Satu kegagalan tidak hentikan yang lain; balas per-ref.

Subscriptions

list_subscriptions

Senarai langganan akaun: pelanggan (emel), produk, jumlah/period, status (active/past_due/cancelled), tarikh caj seterusnya. Tapis ?status, page/limit.

cancel_subscription

Batalkan langganan pelanggan. Kad token kekal pada merchant — boleh aktif semula.

retry_subscription

Cuba 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_card

Kemas 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_override

Set 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_override

Kosongkan override sebuah borang — kembali guna kredensial default akaun.

get_form_chip_feeds

Baca senarai feed CHIP borang (issue #208). Setiap feed ada brand_id + secret (tersembunyi) + logik bersyarat. Feed tanpa condition = lalai (sentiasa padan).

set_form_chip_feeds

Set 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_mapping

Baca peta medan CHIP borang (medan CHIP → id blok borang). Kosong = guna lalai (name→full_name, email→email, phone→phone).

set_form_chip_mapping

Set 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_settings

Baca status kredensial CHIP akaun: configured, verified, mode, brand_id, secret_key_masked, email_fallback, whitelist. Secret penuh tidak pernah dipulangkan. Read-only.

set_chip_settings

Set kredensial CHIP akaun (brand_id + secret_key). Secret kosong = kekalkan kunci sedia ada. Disahkan terhadap CHIP sebelum disimpan. Admin sahaja.

clear_chip_settings

Kosongkan kredensial CHIP akaun. Admin sahaja.

Form email & notifications

get_form_email_settings

Baca 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_settings

Set 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_overrides

Padam SEMUA override emel per-borang supaya borang kembali mewarisi tetapan emel workspace (issue #453).

get_form_notifications

Baca 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_notifications

Ganti 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_confirmations

Baca pengesahan borang (issue #179): senarai confirmation (message/redirect) + logik bersyarat. Pengesahan lalai sentiasa wujud.

set_form_confirmations

Set 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_message

Baca mesej pembayaran-gagal borang (issue #393): apa yang pembeli lihat bila pembayaran gagal. Kosong = guna mesej lalai.

set_form_failure_message

Set mesej pembayaran-gagal borang (issue #393): HTML yang pembeli lihat bila pembayaran gagal (disanitasi, tiada XSS). Kosong = guna mesej lalai.

Email settings

get_email_settings

Baca tetapan emel satu workspace: subjek/badan resit (placeholder), hantar resit, notifikasi merchant, HTML.

set_email_settings

Set 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_einvoice

Set TIN + MSIC LHDN untuk resit e-Invoice (B2B).

get_einvoice_settings

Baca TIN + MSIC LHDN semasa untuk resit e-Invoice.

Webhooks

create_webhook

Daftar 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_webhooks

Senarai outbound webhooks akaun. form_id pilihan: set = webhook borang itu sahaja; kosong = webhook seluruh workspace/akaun.

delete_webhook

Padam satu webhook (id dari list_webhooks).

update_webhook

Kemas kini webhook (url, events, secret pilihan — kosong = kekal sedia ada). events: sale.paid|sale.failed|subscription.charged|*.

webhook_deliveries

Log penghantaran terkini webhook (status_code, attempt, error).

retry_webhook_delivery

Cuba semula satu penghantaran webhook yang gagal dengan segera (id dari webhook_deliveries). Ia dienqueue semula dan dihantar semula pada sweep seterusnya.

get_webhook_signing_key

Baca 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_keys

Senarai semua API key akaun (id, nama, scopes, created_at, last_used). Key material tidak pernah dikembalikan.

create_api_key

Cipta 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_key

Padam 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_affiliate

Cipta affiliate untuk workspace: kod rujukan (?aff=CODE), nama, emel, kadar komisen. Jualan yang dibawa affiliate dikreditkan kepadanya.

list_affiliates

Senarai semua affiliate dalam satu workspace — id, kod, nama, komisen, URL rujukan.

get_affiliate_sales

Senarai jualan yang dibawa oleh satu affiliate (read-only, skop workspace).

set_affiliate_commission

Set kadar komisen affiliate (cth: 10 = 10%).

Workspaces & storefronts

list_workspaces

Senarai semua workspace (storefront) anda — id, nama, slug dan URL.

create_workspace

Cipta 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_workspace

Tukar 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_settings

Baca 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_settings

Set 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_page

Baca 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_page

Set '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_qr

Jana 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_page

Baca 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_page

Set 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_rates

Kadar tukaran wang BNM (Ringgit per unit mata wang asing), dicache harian. Guna untuk paparan/penukaran amaun baharu.

set_workspace_currency

Set mata wang workspace (default MYR). Perubahan terpakai pada produk/jualan baharu; jualan lama kekal mata wang asal (tidak ditukar balik).

check_workspace_deletion

Semak 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_workspace

Hantar 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_workspaces

Senarai 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_workspace

Pulihkan 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_workspace

Padam 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_member

Jemput 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_members

Senarai ahli workspace + peranan (admin sahaja).

update_workspace_member_role

Tukar peranan ahli workspace (admin sahaja). Role: read_only|manage|admin.

remove_workspace_member

Buang ahli dari workspace (admin sahaja). Pemilik tidak boleh dibuang.

list_workspace_invites

Senarai jemputan menunggu untuk workspace (admin sahaja).

cancel_workspace_invite

Batal jemputan menunggu untuk workspace (admin sahaja).

set_default_workspace

Set 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_phone

Set nombor telefon pilihan pada profil anda (kosong = buang). Format longgar: digit, +, ruang, 8-15 digit.

update_profile_timezone

Set zona waktu IANA pilihan pada profil anda (kosong = Auto, ikut pelayar/PC). Contoh: Asia/Kuala_Lumpur, UTC.

update_profile_language

Set 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_avatar

Set avatar profil (URL mesti imej yang dihoskan JomForm dari upload bucket). Kosong = buang avatar.

list_passkeys

Senarai passkey (WebAuthn) akaun anda: id. Read-only.

delete_passkey

Padam satu passkey (id dari list_passkeys).

list_social_accounts

Senarai akaun sosial yang dipautkan (google|azure_ad|github|cloudflare): provider, emel, nama, picture, linked_at. Read-only.

unlink_social_account

Nyah-pautkan akaun sosial (provider dari list_social_accounts).

get_2fa_status

Baca status 2FA akaun anda: enabled (true/false). Read-only.

setup_2fa

Jana rahsia TOTP untuk 2FA (belum aktif). Pulangkan secret + otpauth_url untuk diimbas aplikasi authenticator, kemudian panggil enable_2fa dengan kod.

enable_2fa

Aktifkan 2FA dengan kod 6-digit dari aplikasi authenticator (selepas setup_2fa).

disable_2fa

Matikan 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_devices

Senarai peranti dipercayai 2FA akaun anda (label, ip, first_seen, expires_at). Peranti dipercayai melangkau kod 2FA selama 30 hari. Read-only.

revoke_trusted_device

Batalkan 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_devices

Batalkan SEMUA peranti dipercayai akaun anda ("log keluar di semua tempat"). Setiap peranti akan meminta kod 2FA semula.

Feedback

list_feedback

Senarai 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_thread

Baca 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_feedback

Balas 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_status

Tukar status thread maklum balas (open|in_progress|resolved|closed). Keputusan triage — super-admin sahaja; diaudit. Status adalah per-thread, bukan per-mesej.

retry_feedback_notify

Hantar 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_feedback

Failkan 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_page

Jana 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_page

Baca satu halaman undang-undang workspace (privacy|terms|refund): mode (auto/manual) + kandungan.

set_legal_page

Simpan kandungan halaman undang-undang workspace (privacy|terms|refund) dengan mode auto|manual. Kandungan dipaparkan di {workspace}.jomform.com/privacy|terms|refund.

Event tracking

list_events

Log 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_deliveries

Status 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_submissions

Senarai respons (submission) untuk satu borang respons-sahaja (mode 'response', tanpa bayaran). Pulangkan data medan + masa. Tapis form_id, page/limit.

Spam review

list_spam_submissions

Senarai submission yang ditanda spam oleh AI (issue #245): data medan, sebab spam, masa. Boleh tapis page/limit. Read-only.

unspam_submission

Nyah-tanda spam pada submission (false positive). Write tool (peranan manage/admin).

list_spam_sales

Senarai jualan yang ditanda spam oleh AI (issue #245): ref, status, jumlah, pembeli, sebab spam, masa. Boleh tapis page/limit. Read-only.

unspam_sale

Nyah-tanda spam pada jualan (false positive). Write tool (peranan manage/admin).

Media

list_media

Senarai media yang dimuat naik akaun (perpustakaan media): thumbnail/url, nama, tarikh, saiz. Boleh tapis form_id dan source (merchant|customer). Read-only.

upload_media

Muat 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_media

Hantar 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_media

Senarai 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_media

Pulihkan 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_media

Padam 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_templates

Senarai template borang sistem (gallery): slug, tajuk, kategori, mode (payment/response). Guna dengan use_template untuk quickstart.

use_template

Clone 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_templates

Senarai template anda sendiri (issue #457): id, slug, tajuk, config, theme. Berbeza daripada list_templates (gallery sistem).

create_template

Simpan 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_template

Kemaskini template anda sendiri (issue #457): tajuk, config dan/atau theme. Omit medan yang tak berubah. template_id dari list_own_templates.

delete_template

Padam (soft-delete) template anda sendiri (issue #457). template_id dari list_own_templates. Boleh dicipta semula bila-bila masa.

Analytics

get_form_analytics

Analitik 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_analytics

Analitik per-produk: unit terjual (paid), hasil (RM). Merentas semua workspace anda. Read-only.

Custom domains

list_domains

Senarai domain tersuai akaun: id, domain, provider, verified, auto_cname. Read-only.

add_domain

Tuntut domain tersuai baharu (cth: kedai.com). Kemudian set CNAME → custom.jomform.com dan panggil verify_domain.

verify_domain

Sahkan domain tersuai (id dari list_domains). Hanya disahkan bila domain benar-benar memaparkan halaman JomForm.

remove_domain

Buang tuntutan domain tersuai (id dari list_domains).

auto_cname_domain

Cipta rekod CNAME Cloudflare secara automatik untuk domain (perlu Cloudflare dipautkan). Pulangkan auto_cname=created|failed.

get_domain_tls_email

Baca emel ACME/Let's Encrypt per-domain untuk auto-TLS. Kosong = kembali ke emel akaun, kemudian ACME_EMAIL global. Read-only.

set_domain_tls_email

Tetapkan emel ACME/Let's Encrypt per-domain untuk auto-TLS. Kosong = kembali ke emel akaun, kemudian ACME_EMAIL global.

set_domain_tls_mode

Tetapkan 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_cert

Muat naik sijil TLS sendiri (bring-your-own-cert) untuk domain: cert + key dalam format PEM. Domain disajikan sijil ini.

Realtime notifications

get_realtime_status

Baca 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_sound

Hidup/matikan bunyi beep notifikasi masa nyata satu workspace (true = bunyi bila jualan/respons baharu tiba; false = senyap).

Tracking

get_tracking

Baca skrip penjejakan global akaun (Google Analytics, Facebook Pixel, dll) yang disuntik ke <head> borang awam. Read-only.

set_tracking

Set skrip penjejakan global akaun. Hanya <script src=https://...>, <meta> dan komen dibenarkan (disanitasi).

System settings

get_system_settings

Baca semua tetapan sistem platform (site_*, landing_*). Read-only.

set_system_setting

Set satu tetapan sistem (key mesti bermula site_ atau landing_).

Cloudflare

get_cloudflare_status

Baca status pautan Cloudflare akaun: linked, configured. Read-only.

connect_cloudflare_api_token

Pautkan Cloudflare via API token (disahkan serta-merta, disimpan terenkripsi).

connect_cloudflare_global_key

Pautkan Cloudflare via global API key (disahkan serta-merta, disimpan terenkripsi).

disconnect_cloudflare

Nyah-pautkan Cloudflare dari akaun.

Account

get_account

Maklumat asas akaun (nama, slug, CHIP mode).

Admin (super-admin, platform-wide)

purge_unverified_accounts

Laporkan 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_workspaces

Senarai SEMUA workspace (storefront) platform: id, nama, slug, emel pemilik, tarikh cipta, bilangan borang + produk. Super-admin sahaja; read-only; diaudit.

admin_media_usage

Penggunaan storan media per workspace (bait) berbanding kuota 1GB setiap workspace. Super-admin sahaja; read-only; diaudit.

admin_list_sales

Senarai jualan merentas SEMUA workspace, boleh tapis (date_from/date_to, workspace_id, status). Super-admin sahaja; read-only; diaudit.

admin_spam_queue

Senarai semua item spam (submission + jualan) merentas semua workspace. Super-admin sahaja; read-only; diaudit.

admin_email_deliveries

Status 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_events

Senarai 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_event

Hidupkan 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_health

Kesihatan sistem: uptime, bilangan ralat MCP terkini (24j), kedalaman queue emel. Super-admin sahaja; read-only; diaudit.

admin_list_suppressions

Senarai 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_suppression

Tambah 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_suppression

Buang 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).