דלגו לתוכן
FillFaster

קוד מותאם אישית עם AI

טפסים ב-FillFaster יכולים להריץ קוד מותאם אישית. משתמשים בו לכללים שבונה הטפסים לא מכסה, למשל:

  • להציג שדות רק כשמסמנים תיבת סימון או עונים “כן” על שאלה.
  • להפוך שדות לחובה רק כשהם מוצגים.
  • להציג שדות נוספים לחלק מהאפשרויות ברשימה.
  • למלא שדה אחד לפי שדה אחר, או לנעול שדה שמולא מראש.
  • לבדוק תשובה לפי כלל משלכם, למשל גיל מינימלי.

לא צריך לכתוב את הקוד בעצמכם. עוזר AI (ChatGPT, Claude, Gemini, Cursor ואחרים) יכול לכתוב אותו לפי הקישור לטופס והתנאים שלכם.

בקשו מעוזר AI לכתוב את הקוד

Section titled “בקשו מעוזר AI לכתוב את הקוד”

העתיקו את הפרומפט, מלאו את הקישור לטופס ואת התנאים, ושלחו לעוזר:

קרא ופעל לפי https://help.fillfaster.com/.well-known/agent-skills/fillfaster-custom-code/SKILL.md כדי לכתוב קוד מותאם אישית לטופס FillFaster שלי.
קישור לטופס: https://fillfaster.com/fill/<מזהה הטופס>
תנאים:
- <למשל: להציג את "שם בן/בת הזוג" ו"ת.ז בן/בת הזוג" רק כשמסומן "נשוי/אה", ולהפוך אותם לחובה>

העוזר קורא את השדות האמיתיים של הטופס, שואל על כל דבר לא ברור, ומחזיר בלוק קוד אחד עם רשימה קצרה של הכללים שהוא מכסה. אם כבר יש קוד בטופס, הוא מסביר מה הקוד הקיים עושה ושואל אם להוסיף לו או לכתוב אותו מחדש.

  1. פתחו את הטופס בבונה הטפסים.
  2. עברו להגדרות ואז להגדרות מתקדמות.
  3. הדביקו את הקוד בשדה קוד HTML מותאם אישית (אם העוזר כתב את הקוד מחדש, החליפו את מה שהיה שם).
  4. אם העוזר השתמש בשדות שהם חובה רק כשהם מוצגים, הגדירו את השדות האלה כלא חובה בבונה הטפסים, כמו שהוא מסביר.
  5. שמרו את הטופס.

אי אפשר לקרוא בדרך הזו טפסים עם קוד גישה ותבניות עם כמה חותמים. לטפסים כאלה פנו לתמיכה של FillFaster במייל support@fillfaster.com וצרפו את מזהה הטופס.

שאר העמוד הוא המדריך שהעוזר עובד לפיו (באנגלית). הוא זמין גם כקובץ Markdown בכתובת custom-code-reference.md.

Custom Code: window.ffFormAPI

Overview

FillFaster forms can run custom HTML/JavaScript from the form settings (htmlCode). That code runs in the filler’s browser, typically to show, hide or require fields based on other answers.

Hiding a field with CSS does not change its validation. A field marked required: true still blocks submission when hidden.

window.ffFormAPI is a small, stable bridge from custom code into the live form. It lets form scripts show and hide fields, toggle required state, read and write values, run validity checks and add validators.

Availability

Condition window.ffFormAPI
Form has custom code (htmlCode is not empty) Defined, for the current form
Form has no custom code Not defined
Form reset (after a submit or a created link) Re-attached to the new form

Custom code lifecycle

  1. The form loads and ffFormAPI is attached.
  2. The <script> and <style> elements of the custom code are added to the page.
  3. When the questions are rendered, the page posts window.postMessage({ action: 'form_questions_loaded' }).

New code: put everything inside ffFormAPI.ready(...) (see Conditional logic helpers). It waits for that message, and runs right away if it already arrived:

window.ffFormAPI && window.ffFormAPI.ready(function () {
// Your logic here
});

Older code waits for the message itself. It keeps working:

window.addEventListener('message', function (event) {
if (event.data && event.data.action === 'form_questions_loaded') {
if (!window.ffFormAPI) {
console.error('ffFormAPI is not available on this form.');
return;
}
// Your logic here
}
});

Field keys vs row ids

Purpose Identifier Example
ffFormAPI methods Field key (the field’s name) field48, field10 copy
The field’s row on the page Row id on .form-row field-field48, field-field10 copy

Pattern: row id = field- + field key. In new code use ffFormAPI.getRow(key) instead of building the id.

Writing custom code (agents and developers)

1. Get the real fields first

GET https://api.fillfaster.com/v1/form/{fid}/custom-code-context

No token. fid is the id in the form link (fillfaster.com/fill/{fid}). Returns { fid, formName, lang, schemas, htmlCode }: the form’s fields and its current custom code. Forms with an access code or 2+ signers return 403 form_not_public; ask the customer to contact FillFaster support (support@fillfaster.com) with the form id.

Never write a field key that is not in this response. If a field the customer describes is missing or ambiguous, ask which field they mean (list fieldName + name). Do not guess.

2. Which entries are fields

schemas is one array per PDF page. Each entry’s name is the field key and fieldName is the label the filler sees. The form turns them into questions like this:

  • Same label, one field. Entries that share a fieldName (the same answer printed in several places) become one question, keyed by the first entry’s name. Use that key.
  • No row: type: 'helper' entries, and anything without a validationType.
  • Row, always hidden: validationType: 'date_today' and entries with hideInput: true (filled by the sender or prefill).
  • Row, no value: validationType: 'html' / 'external_link' (static text). showFields / hideFields / getRow work on them; value and validation methods do not.
  • multi_variable_text: one text field per variable, keyed {name}_{variable}, labelled with the variable name.
  • Value types: checkbox true/false; yes/no the translated word (use isYes / isNo); select and radio the option text; date a Date; file/image an array; table an array of row objects.
  • required is the starting state. For a field that is required only when shown, set it to not required in the form builder and use showWhen(..., { required: true }).

3. Pick the helper for the use case

Customer asks Write
Show fields when a checkbox is checked api.showWhen('cb', true, [...])
Show fields when a yes/no answer is Yes (or No) api.showWhen('q', true, [...]) / api.showWhen('q', false, [...])
…and make them required while shown add { required: true }
Show fields for some select / radio options api.showWhen('sel', ['A', 'B'], [...])
Show fields when a field has any value (date, text) api.showWhen('f', function (v) { return !!v; }, [...])
Show the next group once the previous one is filled (child 1, 2, 3…) chain showWhen from a key field of each group to the next group
Fill one field from another (select → code) `api.onValueChange(‘sel’, function (v) { api.setValue(‘code’, MAP[v]
Only one of several checkboxes onValueChange on each; when one turns true, setValue(other, false) on the rest
Lock a prefilled field api.setReadOnly('f', true)
Custom validation (age, unique ID, phone) api.addValidator('f', fn); see addValidator, and revalidateFields(..., { markTouched: 'invalid' }) for cross-field rules
A “continue” button that opens a section only when fields are valid areFieldsValid + revalidateFields(..., { markTouched: true }), then showFields

Everything goes inside one ready call:

<script>
window.ffFormAPI && window.ffFormAPI.ready(function () {
var api = window.ffFormAPI;
// One line per rule. Comment each rule with the labels it uses.
});
</script>

4. Rules

  • Read and write values only through ffFormAPI, never through input.value, dispatchEvent, widget CSS classes, MutationObserver, label text or localStorage. The page markup is not part of the contract and has changed before.
  • Never compare yes/no answers to 'כן' / 'Yes'. Use isYes / isNo or showWhen(key, true|false, ...).
  • Do not hide rows in CSS; showWhen / hideFields hide them on load. CSS is for layout and styling only.
  • No setTimeout to wait for the form. ready already waits.
  • Keep everything inside the ready callback so nothing leaks into globals.
  • Custom code is not a security boundary. It runs in the filler’s browser.
  • Multiple signers: when a template with several signers has custom code, the server checks only signature fields as required for each signer. Required rules for other fields are enforced by the form only.

5. When the form already has custom code

htmlCode in the context response is the code on the form now.

  1. Read it and list, in plain words, what each part does.
  2. Ask the customer: add the new rule and keep the existing code as it is, or rewrite the code to the helpers above (same behavior, shorter, and it keeps working when the form changes)?
  3. If rewriting: keep every rule’s behavior, map each old pattern to its helper (table below), and keep anything that has no helper (custom CSS, signature-pad tweaks, iframes) unchanged.
  4. Return the full new code, not a diff, with a short list of what changed.
Old pattern Helper
window.addEventListener('message', ... 'form_questions_loaded') ffFormAPI.ready(fn)
showHideListOfFields([...rows], 'show'/'hide') + setRequired + setValue(key, '') showFields / hideFields(keys, { clear: true })
input.checked, radio value === 'כן', getValue(key) === 'Yes' isYes(key) / showWhen(key, true, ...)
MutationObserver or click listeners on a select’s markup onValueChange(key, fn)
input.value = x; input.dispatchEvent(new Event('input')) setValue(key, x)
document.getElementById('field-' + key) getRow(key)
input.readOnly = true setReadOnly(key, true)
Finding fields by label text the name from the context response

6. What to hand back

  • One block to paste into the form builder: Settings → Advanced Settings → Custom HTML Code (Hebrew UI: “הגדרות מתקדמות” → “קוד HTML מותאם אישית”): <script> (and <style> only if needed).
  • Under it, a short list: each rule in the customer’s words, with the field labels it uses.
  • Tell the customer to set the conditional fields to not required in the builder when you used { required: true }, and to test the form link before sending it.

Patterns without a helper

Hide “Receive a copy by email” on the success screen, keep Download (CSS on a page class; re-check if the success screen changes):

<style>
.get-submitted-pdf-container > *:nth-child(n+4) { display: none; }
</style>

Dynamic checkbox list written into a text field (options depend on a select or a table): build the checkboxes inside getRow(textKey), hide the text input, and on every change api.setValue(textKey, checked.join(', ')).

Signature pad size or colour: possible, but it depends on page internals and can break when the form changes. Write it only when the customer asks, and test on mobile.

Embedding the form in an iframe and listening to its events is not custom code; it is the embed flow (window.parent.postMessage with status: 'submitted_successfully').

API reference

All methods log a console warning and return false when the field key is missing (unless noted).

setRequired(fieldKey, isRequired)

Makes a field required or optional. Its other checks (ID number, phone, email, pattern) stay as they are. The asterisk next to the label follows the change.

window.ffFormAPI.setRequired('field48', true); // make required
window.ffFormAPI.setRequired('field48', false); // make optional

Returns: boolean: whether the field was found.

areFieldsValid(fieldKeys)

Returns true only if every listed field is currently valid. Missing fields are treated as valid.

var ok = window.ffFormAPI.areFieldsValid(['field1', 'field2', 'field3', 'field4']);
if (!ok) {
window.ffFormAPI.revalidateFields(['field1', 'field2', 'field3', 'field4'], { markTouched: true });
}

Returns: boolean

revalidateFields(fieldKeys, options?)

Re-runs the checks on the listed fields.

options.markTouched Behavior
omitted / false Revalidate only
true Revalidate and show errors on all listed fields
'invalid' Revalidate all; show errors only on invalid fields

Use 'invalid' for cross-field rules (e.g. uniqueness) so sibling fields refresh without marking valid ones.

// Cross-field uniqueness: refresh siblings, show errors only on duplicates
window.ffFormAPI.revalidateFields(['field2', 'field10', 'field10 copy'], { markTouched: 'invalid' });
// Gate a section: show errors on every invalid field in the list
window.ffFormAPI.revalidateFields(['field1', 'field2', 'field3', 'field4'], { markTouched: true });

Returns: void

getValue(fieldKey)

Reads the field’s current value (e.g. Date for date fields, string for text).

Returns: the value, or undefined if not found.

setValue(fieldKey, value)

Sets the value, marks the field as touched, and re-runs its checks. Use when clearing a hidden section (e.g. after removing a dynamic row).

window.ffFormAPI.setValue('field12', '');

Returns: boolean: whether the field was found.

addValidator(fieldKey, validatorFn)

Adds a check from custom code.

  • validatorFn(value) must return null (valid) or a string error message (invalid). The message is shown under the field.
window.ffFormAPI.addValidator('field12', function (value) {
if (!value) return null; // let required handle empty
var d = new Date(value);
if (d > new Date()) return 'לא ניתן לבחור תאריך עתידי';
// ... age check, etc.
return null;
});

Note: Each call adds another check. Register once per field.

Returns: boolean: whether the field was found.

Conditional logic helpers

These cover most custom code: show, hide and require fields by another field’s value. They read the form’s values, not the page markup, so they keep working when the form’s look changes. Prefer them over DOM listeners, MutationObserver, label-text lookups and setTimeout.

Compatibility contract: custom code on live forms calls these names. Only add methods or options. Never rename one or change what it does.

ready(cb)

Runs cb once the questions are rendered, or right away if they already are. Call every other helper inside it.

onValueChange(fieldKey, cb)

Calls cb(value) now with the current value (prefill included) and again on every change. Listeners stay active when the form resets after a submit or a created link, and rerun on the new form. Returns: boolean.

showFields(fieldKeys, options?) / hideFields(fieldKeys, options?)

Show or hide the rows (#field-{key}) and keep validation in step:

  • Hiding removes required, so a hidden field never blocks submit. Showing it again restores required if it had it.
  • showFields(keys, { required: true }) also makes the fields required (not required in the builder + shown = required).
  • hideFields(keys, { clear: true }) empties the hidden fields. It does not clear while rules are first applied on load, so prefilled values survive.

Showing removes the row’s inline display style, so fields hidden in the form builder stay hidden. Do not hide rows with CSS in custom code: a CSS-hidden row stays hidden after showFields. Let showWhen / hideFields hide them on load.

Keys of headings or static text have a row and no value; they are shown and hidden like any field.

showWhen(triggerKey, condition, fieldKeys, options?)

Shows fieldKeys while triggerKey’s value matches condition, hides them otherwise. Built on onValueChange, so it runs on load and after every change.

condition Matches when
true yes/no is Yes, or checkbox is checked (isYes)
false yes/no is No, or checkbox is unchecked (isNo). Unanswered yes/no matches neither
a value, e.g. 'Married' value equals it (select, radio, text)
an array of values value is one of them
function (value) { ... } the function returns truthy

options: { required: true } as in showFields; clear defaults to true (hidden answers are not submitted). Pass { clear: false } to keep them. Returns: boolean (trigger found).

isYes(fieldKey) / isNo(fieldKey)

Yes/no answers are stored as the translated word ('Yes' in English, 'כן' in Hebrew), and checkboxes as true/false. Comparing to a literal word breaks when the form language changes. Use these instead; they accept both forms in any language. A yes/no question nobody answered is neither yes nor no.

setReadOnly(fieldKey, readOnly)

Locks or unlocks a field. The value is still submitted, and a locked field is not validated. Stays locked after the form resets. Returns: boolean.

getRow(fieldKey)

Returns the field’s row element (#field-{key}) or null. Use it instead of building the id yourself when you need the page element, e.g. to insert a button after a row.

Example

window.ffFormAPI && window.ffFormAPI.ready(function () {
var api = window.ffFormAPI;
// Married checkbox → spouse details, required while shown
api.showWhen('field39', true, ['field24', 'field25'], { required: true });
// Yes/no "has a third party?" → third-party details
api.showWhen('field41', true, ['field3 copy', 'field5 copy', 'field12']);
// Select → extra fields for two of its options
api.showWhen('field15', ['Purchase', 'Sale'], ['field74', 'field82']);
// Prefilled vehicle number stays as sent
api.setReadOnly('vehicle_number', true);
});

Typical patterns

Gate a section until employee fields are valid

continueBtn.addEventListener('click', function () {
var keys = ['field1', 'field2', 'field3', 'field4'];
if (!window.ffFormAPI.areFieldsValid(keys)) {
window.ffFormAPI.revalidateFields(keys, { markTouched: true });
return;
}
window.ffFormAPI.showFields(['field48', 'field10', 'field11'], { required: true });
});

Security and scope

  • The API exists only on forms whose owner added custom code.
  • It works on the current form only.
  • It does not bypass server-side checks; anything security-critical must be checked on the server.