Documentation
A FreeCRM form can be published in several ways, from pasting a link into an email to rendering the form inside your own website with your own styling. This page covers each method, what it can and cannot do, and the settings that apply to all of them.
If you only need a link to send to people, you do not need any of this — see Forms for creating and sending forms.
| Method | The form is | Styling | Best for |
|---|---|---|---|
| Link | A page we host | Ours, plus your custom CSS | Sending to contacts, social posts, QR codes |
| iframe | A page we host, in a frame | Ours, plus your custom CSS | Dropping a form on a site with no development work |
| HTML embed | Rendered into your page | Your site’s CSS | A form that should look like the rest of your site |
| Your own markup | Written by you | Entirely yours | Full control of layout, and attaching files you generate |
| React / Vue / JavaScript | Rendered into your app | Your app’s CSS | Single page applications |
Every method submits to the same place and produces the same submission, contact and record updates.
Open the form, click Settings, and scroll to Embed this form. Pick a tab and copy the snippet — it already contains your form’s id, its access code and, for the “Your own markup” tab, one input per field of your form.
The embed script itself is served from a versioned, permanent URL:
https://api.cogmento.com/embed/v1/form.js
Loading it does two things: it renders any element on the page that carries a data-crm-form or data-crm-attach attribute, and it exposes a small CRMForms API for pages that manage their own lifecycle.
Every form has a public address:
https://forms.freecrm.com/form/FORM_ID/ACCESS_CODE/
Send it to anyone. A submission with no contact attached creates a new contact; see Forms for how sending a form to a known contact updates that contact instead.
The simplest embed, and the only one that needs no JavaScript:
<iframe src="https://forms.freecrm.com/form/FORM_ID/ACCESS_CODE/"
style="width:100%;min-height:600px;border:0"></iframe>
The form inside the frame is styled by us — you can change its appearance with the form’s own Custom CSS setting, but it will not inherit your site’s fonts and colours. In exchange, every field type works, including file uploads, signatures and multi-page forms.
Paste one element and one script tag where the form should appear:
<div data-crm-form="FORM_ID" data-access-code="ACCESS_CODE"></div>
<script src="https://api.cogmento.com/embed/v1/form.js" async></script>
The script fetches the form and renders it as ordinary HTML inside your page — no iframe and no injected stylesheet — so it inherits your site’s CSS. Style it through the _crm_ classes listed under Styling below.
Optional attributes:
data-redirect — send the visitor to this URL after a successful submission.data-submit-label — text for the submit button; defaults to “Submit”.Some fields are too rich to redraw faithfully inside someone else’s page. If your form contains any of the following, the embed quietly replaces itself with an iframe of the hosted page rather than rendering the form wrongly:
Everything still works — you simply get our styling for that form instead of yours. Forms built only from text, long text, email, phone, URL, date, hidden, dropdown, radio, checkbox and content blocks render inline.
For complete control, write the form yourself and let the script handle validation, submission and errors. Tag each input with the CRM field it feeds:
<form data-crm-attach="FORM_ID" data-access-code="ACCESS_CODE">
<label for="first-name">First name</label>
<input id="first-name" data-crmid="first-name" required>
<span data-crm-error="first-name"></span>
<label for="email">Email</label>
<input id="email" data-crmid="email" required>
<span data-crm-error="email"></span>
<button type="submit">Send</button>
</form>
<script src="https://api.cogmento.com/embed/v1/form.js" async></script>
The value in data-crmid is the field’s key: a stable, readable handle derived from the field’s title the first time it is given one — “First name” becomes first-name, “Company” becomes company. Two fields with the same title get email and email-2. A field you never title has no key, and the snippet uses its id instead.
Three things worth knowing:
email-2 means “the second field that wanted to be called email”, not “the second field on the form”.The exact key for every field is in the snippet on the Embed this form panel.
When the submission is accepted the form’s outro text is shown, so a visitor is never left looking at the form they just sent.
Mark where it should appear and the message is written there, and the form is hidden:
<div data-crm-success hidden></div>
With no such element, the form itself is replaced by the message. Either way you can style it — the inserted block carries the _crm_success class.
Two ways to take over instead:
redirectUrl (or data-redirect) and the visitor is sent to your own page.showSuccess: false to CRMForms.attach and nothing is touched; use onSuccess to do your own thing. onSuccess on its own does not suppress the message, since it is just as often only an analytics hook.If a page holds more than one form, name the one a slot belongs to: <div data-crm-success="FORM_ID">.
Add <span data-crm-error="KEY"></span> anywhere in your form and validation messages for that field are written into it. Fields with no slot simply have no message rendered — the script never inserts elements into your layout uninvited.
If your page names a field the form does not have, or omits one the form does, the script writes a warning to the browser console naming the field. Nothing is dropped silently.
A file input works here like any other field — tag it with the file field’s key and the script sends the file with the submission:
<input type="file" data-crmid="attachment">
<span data-crm-error="attachment"></span>
You can also attach a file your own code produced, rather than one the visitor picked. Hide the input and fill it in from JavaScript:
<input type="file" data-crmid="attachment" hidden>
function attachGeneratedFile(blob, filename) {
const transfer = new DataTransfer()
transfer.items.add(new File([blob], filename, { type: 'application/pdf' }))
document.querySelector('[data-crmid="attachment"]').files = transfer.files
}
The script reads the input’s files rather than its value, so it cannot tell the difference between a file a visitor chose and one you assigned — both submit identically. Build the file before the form is submitted; there is nothing to wait for beyond that.
One file per field. The field’s Allowed file types and Maximum file size still apply, and a file that fails either is refused with a message in that field’s error slot.
The framework tabs are wrappers around the same two calls, for apps that control their own mounting and unmounting:
// We render the form into your element
const handle = CRMForms.mount(element, {
formId: 'FORM_ID',
accessCode: 'ACCESS_CODE',
onSuccess: (submission) => { /* ... */ },
onError: (error) => { /* ... */ },
})
handle.destroy() // on unmount
// You render it, we handle submission
const handle = CRMForms.attach(formElement, { formId, accessCode })
// You handle everything, we just post the values
CRMForms.submit({ formId, accessCode }, { 'first-name': 'Ada', email: 'ada@example.com' })
Options accepted by mount and attach:
| Option | Purpose |
|---|---|
formId, accessCode |
Required. Both are in the snippet. |
onSuccess(result) |
Called with the submission after it is accepted. |
onError(error) |
Called when the submission is rejected. |
redirectUrl |
Sends the visitor here after success. |
submitLabel |
Submit button text (mount only). |
showSuccess |
false suppresses the confirmation message (attach only). |
data |
Extra values posted with every submission. |
apiBase |
Override the API origin. Rarely needed. |
CRMForms.version and CRMForms.prefix report the script version and the CSS class prefix.
A plain HTML form can post straight to the API and be sent on to your own thank-you page:
<form method="post" action="https://api.cogmento.com/api/1/form/FORM_ID/ACCESS_CODE/">
<input name="first-name">
<input name="email">
<input type="hidden" name="_redirect" value="https://example.com/thanks">
<button type="submit">Send</button>
</form>
Field names are keys or ids, exactly as with data-crmid. Validation errors come back as JSON rather than a rendered page, so this suits forms you are confident about. _redirect only accepts ordinary http and https addresses.
Everything the HTML embed renders carries a class prefixed with _crm_, and nothing else — no inline styles, no stylesheet of ours to fight with:
._crm_form { } /* the form element */
._crm_field { } /* one field's wrapper */
._crm_label { }
._crm_input { } /* inputs, selects, textareas */
._crm_hint { } /* a field's description */
._crm_options, ._crm_option, ._crm_choice { } /* radio and checkbox groups */
._crm_required { } /* the asterisk on a required label */
._crm_error, ._crm_formError { }
._crm_actions, ._crm_submit { }
._crm_captcha { }
._crm_success { } /* the thank-you block after submission */
These class names are part of the published interface — they will not be renamed within v1.
By default a form can be embedded anywhere. Listing sites under Allowed websites in the form’s settings restricts it to those sites — one per line, as a full origin (https://example.com), a bare domain (example.com), or a wildcard for subdomains (*.example.com).
When the list is set it governs three things: which sites may read the form’s definition, which may submit it, and — if the captcha is on — where the captcha may be solved. List every site you embed the form on, or submissions from the others will be refused.
When Captcha is enabled the embed script renders a reCAPTCHA automatically; you do not need to add anything. In the “Your own markup” method you can decide where it goes by placing an empty <div data-crm-captcha></div> inside your form. Leave it out and the captcha is inserted just above your submit button.
Submissions without a solved captcha are refused by the server, so the captcha cannot be bypassed by removing it from the page.
The form’s Custom CSS setting styles the hosted page and anything shown in an iframe. It does not apply to the HTML embed or your own markup, which are styled by your own site.
showSuccess: false.data-crmid the form does not recognise; compare it against the keys in the snippet.