DOCUMENTATION

Everything, in one place.

48 articles across 12 topics.

Getting started

What EmbedSheets does, how the builder is laid out, and how to get your first calculator published.

What EmbedSheets does #

EmbedSheets began as a way to put a Google Sheet on a website as an interactive calculator — your spreadsheet did the arithmetic, and visitors got a proper form instead of a grid.

It has grown well past that. The builder now creates full calculators and forms, with a library of input fields, results, content blocks and widgets, and you can work out the answers three different ways.

Every calculator is made of three things:

  • Inputs — the questions you ask.
  • Calculations — what turns those answers into results.
  • Results — what the visitor sees, and what is emailed to you.

Visitors see answers update without a page reload, wherever the calculation happens.

Three ways to calculate #

There is no single right way. Pick whichever suits the calculation — or use both together.

1. The formula builder

Write the arithmetic here, referring to fields by their keys. Everything runs in the visitor’s browser, so results update the instant they type.

ROUND([room_width] * [room_length] * [price_per_sqm], 2)

Best for pricing, quotes and anything you can express directly. See writing a formula.

2. Google Sheet cell mapping

Keep the logic in a spreadsheet you already maintain. The visitor’s answers are written into the cells you nominate, the sheet recalculates, and the results are read back out.

Best where the logic is already built, where it is genuinely complicated, or where somebody non-technical needs to change the numbers without opening the builder. See connecting a Google Sheet.

3. Both together

The combination is where this gets powerful. Let the sheet do the heavy part — a rate table, a depreciation schedule, a model somebody spent years on — and use formulas here for everything around it: totals, discounts, rounding, the friendly sentence at the end.

ROUND([sheet_base_price] * (1 - [discount] / 100) + [delivery], 2)
A result that comes from a sheet behaves like any other value once it is back, so formulas can read it as easily as they read a field.

Creating your first calculator #

  1. Choose Create in the sidebar.
  2. Pick a template, or choose Blank calculator.
  3. Name it. You can rename it at any time.
  4. The builder opens. Drag a row layout onto the canvas, then drop fields into it.

Work saves as you go. Save in the top bar forces one immediately, and Preview opens the calculator exactly as a visitor will see it.

Finding your way around the builder #

The builder has three parts:

  • Left — the library. Elements holds layouts, sections, inputs, content, results, buttons, and charts and tables; Widgets holds maps, embeds, reviews and the rest.
  • Middle — the canvas, showing the calculator as visitors will see it.
  • Right — settings for whatever you have selected, split into General, Design and Advanced.

Across the top: Options (everything that applies to the whole calculator), Preview, Publish and Save.

What everything is called #

The words this documentation uses, in one place. Every one of them means something specific here, and knowing which is which makes the rest of the manual read faster.

Calculator
Anything you build here, whether it does arithmetic or not. A contact form is a calculator with no formulas in it. One calculator is one thing you publish, and it counts as one against your plan.
Element
One thing on the canvas: a question, a heading, a chart, a button. You drag elements from the panel on the left and they are what a calculator is made of.
Field
An element that asks the visitor something and holds their answer — a number, a dropdown, a date. Every field has a key, which is the name formulas use to refer to it.
Result
An element that shows a figure worked out from the answers, rather than asking for one. What a visitor came for.
Widget
An element that is neither a question nor a result: a chart, a data table, a map, a set of reviews, an API Connector. Some show something, some fetch something.
Row, column and step
How the canvas is arranged. A calculator is a stack of rows; each row is split into columns; elements sit inside columns. A step is a row marked as its own page, which is what turns one long form into several short ones.
Formula
The arithmetic behind a result, written in Excel-style syntax with fields in square brackets — [price] * [quantity]. See writing a formula.
Calculation engine
Whatever works the numbers out. Either the built-in formula engine, or a Google Sheet you have connected, which does the arithmetic with the formulas already in it.
Lead
One submission: who the person was, what they answered, and what came out. Leads are stored in your account and can be emailed, exported, posted to a webhook, or added to a Google Sheet.
Lead capture
Asking for contact details as part of the form — optionally holding the results back until they are given, which is called gating.
Embed code
The snippet you paste into your own website. It puts the calculator on your page in a frame that resizes itself to fit.
Share link
A page of its own for the same calculator, at an address you can send to somebody. No website needed. See publishing.
Access code
Six characters somebody has to type before a share link will open. It guards the shared page only — an embed on your own website opens normally for your visitors.
Template
A finished calculator you can start from instead of a blank canvas. Everything in it is yours to change once you have used it.
Theme
The colours, typeface, spacing and button shapes applied to a whole calculator, so it matches the site it is going on.
Public key
The identifier in a calculator’s published address. It identifies the calculator; it is not a password, and knowing it grants nothing beyond opening a page you published.

Layout: rows, columns and steps

Rows, columns and multi-step forms - arranging a calculator so it reads well on every screen.

Rows and columns #

A calculator is built from rows, each split into columns. Fields live inside columns, so drag a layout on first.

  1. From Elements, drag a row layout onto the canvas — one column, two columns, narrow + wide, and so on.
  2. Drop fields into the columns.
  3. Click the row’s gear icon for Row settings.

Row settings let you change the layout, and set each column’s width and padding individually. Width accepts any CSS length — 320px, 40% — and blank means the column keeps the share the layout gives it. Padding has separate top, right, bottom and left boxes; a plain number is read as pixels.

Columns stack automatically on narrow screens, so a two-column row becomes two full-width rows on a phone. There is no separate mobile layout to build.

Duplicating a row #

Hover a row and press the copy icon in its toolbar. The row is copied whole — columns, fields and settings.

Every copied field is given a new field key, because two fields cannot answer to the same name. Your existing formulas still point at the originals, so check them after duplicating.

Sections: multi-step forms #

A Section turns a calculator into a multi-step form. Drag one from Elements and drop it between rows.

Everything below a section, until the next one, becomes a step with its own Next and Back buttons. Give the section a title and description and they appear at the top of that step.

Anything above the first section stays on screen throughout. Put a banner, a heading or a logo there and it shows on every step rather than only the first.

How a step looks

A step’s settings have a Design tab, separate from the words on the Settings tab. Heading alignment moves the step’s heading and the sentence under it together — left, centre or right.

Showing progress

The Design tab also holds Progress, which applies to the whole calculator rather than to the step you opened it from. There are two switches and they are independent — you can have the graphic, the words, both, or neither:

  • Show a progress bar — the graphic.
  • Show “Step 2 of 4” — the same thing in words, above it.

With the graphic switched on you can choose its Design:

  • Bar — one track that fills as the visitor moves. Its height is yours to set, between 3 and 40 pixels.
  • Numbered steps — a circle per step joined by a line, ticked once a step is behind them.
  • Steps with labels — the same, with each step’s name underneath.

Those names come from Step label in the step’s own settings, which is separate from its heading for a reason: a heading has the width of the calculator to itself, while a label sits under a circle with three others beside it. “Your Savings Report” reads well as one and badly as the other, so give the step a short label and keep the long heading. Leave it empty and the heading is used; leave both empty and the step shows its number.

Alignment moves the whole indicator — caption and graphic together — to the left, the centre or the right. With both a caption and a graphic you can also choose which side the “Step 2 of 4” line sits on.

Three colours are yours to set: the bar colour (the part travelled, and the circles behind the visitor), the background (the part not yet travelled, and the line between circles), and the text colour. Each follows the calculator’s own theme until you choose one — press the × beside a colour to put it back. Whichever design you pick is drawn on the canvas as you choose it, so there is no need to open the preview to see it.

Labels need room. On a narrow screen the step names shrink and then hide, leaving the circles — which still show where somebody is. If your step titles are long, turn on “Step 2 of 4” as well so the position is always readable in words.

A step that a conditional rule has removed leaves the row entirely rather than sitting there greyed out, so the circles and the “of 4” always agree with each other.

Showing and hiding on different screens #

Every element and every row can be shown or hidden per screen size. Open its settings, go to Advanced, and use Show on.

  • Desktop — wider than 768px
  • Tablet — 481px to 768px
  • Mobile — 480px and under
Sizes are measured against the area the calculator is embedded in, not the visitor’s screen. A calculator in a narrow sidebar gets the mobile treatment even on a desktop.

Hidden elements still take part in calculations. Hiding changes what is shown, never what is worked out.

Conditional rules: showing things only when they apply #

A row, an element or a whole step can be shown only when something else is true — a number is over a threshold, a choice was made, a button was pressed. Open its settings and go to the Conditions tab.

Anything that carries rules shows a small branch mark beside its copy and delete icons on the canvas. Clicking it opens this tab, so you can see at a glance which parts of a calculator are conditional.

Writing a rule

A rule reads as a sentence:

If  [Monthly bill]  is greater than  200   then  show

The first box lists everything you can test: input fields, results, and buttons. The second changes to suit what you picked.

What you can test for

  • is greater than, is greater or equal, is less than, is less or equal — numbers.
  • equals, does not equal — numbers or text. A dropdown or radio group is matched on its option value, not the label the visitor reads.
  • contains, does not contain — text, and the tidiest way to test one answer out of a set of checkboxes.
  • is empty, is not empty — whether it has been answered at all. These take no value.
  • is clicked — buttons only. See below.

What happens when it is met

  • Show — hidden until the rule is met.
  • Hide — visible until the rule is met.
  • Toggle — only for a button click. Each press flips it. Choose whether it starts hidden or visible.

More than one rule

Add a second rule and a joining word appears in front of it: AND needs both to be true, OR needs either. The whole list is read left to right, so mixing them freely is not the way to express something complicated — two simple rules on two elements is usually clearer than four on one.

Buttons that only show and hide

To reveal something on a click, give the button the action Click (used for conditional rules). That action does nothing on its own: it exists to be pointed at by a rule. A button set to Calculate or Submit still does its own job and cannot be used this way.

The usual case is a long table you do not want on screen from the start:

Button  "Show amortisation schedule"   action: Click

Row containing the table:
    If  [Show amortisation schedule]  is clicked  then  toggle  (starting hidden)

Things worth knowing

  • A hidden element still takes part in calculations, exactly as a permanently hidden one does. Rules change what is shown.
  • A required field that a rule has hidden is not demanded. A visitor cannot be stopped by a question they cannot see.
  • Rules on a step are read the same way, but a step also has to be the one being shown — a rule cannot pull a step out of turn.
  • Rules are checked as the visitor types, not only when Calculate is pressed.

Input fields

Every input field, what it collects, and the settings that decide how it behaves.

Settings every field shares #

Click any field to open its settings on the right.

General

  • Label — the question the visitor reads.
  • Placeholder — grey hint text inside the box.
  • Default value — what the field starts as.
  • Required — the form cannot be submitted without it.
  • Hidden field — kept in calculations, never shown.

Design — most text-style fields can show an icon inside the box; pick one or switch it off.

Advanced

  • Field key — the name formulas use, for example [loan_amount].
  • Custom CSS class — for styling from your own site.
  • Show on — desktop, tablet, mobile.
The field key is what formulas refer to, and what becomes the column name anywhere your leads end up. Rename it early — changing it later breaks every formula already using the old name.

Dropdowns, radios and checkboxes #

Choice fields store two things per option: the label the visitor reads, and the value your formulas use.

Set the value to whatever that option is worth. A “Premium finish” option might carry the value 1.4, and your formula multiplies by it.

A long list does not have to be typed. Import from CSV under the options reads one choice per line — label, value, icon URL — and a value is optional, so a list where the label is the value needs only one column. Download template beneath it gives you the file to fill in.

  • Dropdown — one choice from a long list. Under Design it can show an icon beside each option, and offer a search box that filters the list as the visitor types.
  • Radio buttons — one choice from a few, all visible.
  • Checkboxes — any number of choices; their values are added up.
  • Multi-select — several choices folded into one dropdown, for when the list is long.
  • Image choices — cards with a picture or icon, for options people recognise by sight.
  • Button choices — a compact row of buttons. Their text size and padding can be set on the field, for the one question that wants bigger or smaller buttons than the rest of the form.
  • Yes / No — a switch. Produces 1 or 0, so it can turn a cost on and off in a formula.

Sliders and number fields #

Use a Slider where roughly right is the point — budgets, sizes, quantities. Use a Number field where the exact figure matters.

Slider settings worth knowing:

  • Minimum, Maximum, Step — the range, and how far each nudge moves.
  • Allow decimal values — lets a typed value carry decimals.
  • Allow values above the maximum — the handle still stops at the maximum, but a typed value may go past it.
  • Let visitors type a value — adds a box beside the slider, because a slider is hard to land on an exact number.
  • Show the value as — standard number, currency or percentage. All three group the thousands; a standard number and a currency also take decimal places.
Formatting is display only. A slider showing “$1,250” still hands 1250 to your formulas.

Name, email, phone and address #

These behave like text fields but are labelled and validated for their purpose, and browsers autofill them correctly.

  • Email — checked for a valid address. Needed if you want to email results to the visitor.
  • Phone — formatted as it is typed. Best when your visitors are all in one country.
  • Phone (international) — adds a country picker, so the dialling code is chosen rather than typed.
  • Address — can suggest real addresses as the visitor types. See address lookup.
  • Country — a picker with flags. The choice can drive pricing, tax or shipping in a formula.

Dates, times and file uploads #

  • Date — a calendar picker. You can limit how far ahead or back visitors may choose, and refuse weekends.
  • Time — chosen from the slots you offer, rather than typed.
  • File upload — lets visitors attach a plan, a photo, a document or a short video. Three switches decide what it takes: Images and Documents are on to begin with, and Video is off until you turn it on — video arrives in hundreds of megabytes, so it is worth asking for deliberately. Turn on Allow more than one file to take several at once, and set how many. Each file may be up to 100 MB, though the field shows whichever is smaller: your setting, or what your server actually permits. Everything attached arrives with the lead, and each file is a link on the leads screen. In a Google Doc template, write the field’s key once — {plan} — and every file lands there: images are drawn into the document scaled to fit the page, and anything else becomes a link to download it. So four files, two photos and two PDFs, give you two pictures and two download links. To place one particular file, add its number: {plan_1}, {plan_2}. Numbers past however many actually arrived are removed rather than printed. Under Advanced you choose how big those images are drawn — 250px wide with the height left on Auto by default, which keeps each picture’s own proportions and puts them one per line.

Product cards #

Product Cards lists things to buy with prices and quantities. Visitors tick what they want and see a running total.

  1. Add the widget and open its settings.
  2. Add products by hand, or press Upload a CSV.
  3. Use Download template to get a file in the right shape.

The CSV columns, in this order:

Product Title,Product Image URL,Available Qty,Product Price
T-shirt,https://example.com/t-shirt.jpg,25,10.00
Poster,,100,20.00

Leave the image column blank and a placeholder is shown instead.

The widget produces the total under its field key, so formulas treat it as an ordinary number. What the visitor actually chose is emailed to you with the lead.

Content and widgets

Headings, prose, images, embedded video and booking widgets - the blocks that are not questions.

Headings, text, images and spacing #

  • Heading — a title with an optional description under it, and an optional icon beside the pair. Set the level, size, weight, colour and alignment under Design.
  • Text — a paragraph for an explanation, note or disclaimer.
  • Image — upload or link a picture. Size it three ways: Natural size, Fill the whole column (matches the column beside it, however long that grows), or Custom width and height with separate values per screen size.
  • Image Header — a banner with the title, sub heading and description over the picture. Includes a darkening overlay, because text over a photograph is unreadable wherever the photograph is light.
  • Spacer / Divider — vertical space, with an optional line through it. Separate heights for wide and narrow screens.

Embedding video, PDFs and other pages #

  • Embed Video — YouTube, Vimeo, or a file you host. Paste the address and it works out which is which.
  • Add Loom Video — a Loom recording, handy for a short walkthrough beside the questions.
  • Embed PDF — a brochure or price list read in place.
  • Embed Iframe — any other web page, for anything the other widgets do not cover.
  • Image slider — a slideshow you upload or link, with optional captions, arrows and dots.
  • Rating Card — collects a star rating including half stars. The number can be used in formulas.

All of these share size settings: width, height, alignment, border and corner rounding.

API Connector: live data from anywhere #

The API Connector calls another service and brings its answer into your calculator: today’s gold price, an exchange rate, a shipping quote, your own pricing service — anything that answers over HTTP with JSON.

You will find it under Widgets. Drag it onto the canvas like anything else. It shows the visitor nothing by default; it exists to fetch figures that the rest of the calculator uses.

Setting up the call

  • API address — the full address, starting with https://.
  • Send asGET puts your values in the address, POST puts them in the body. The service’s documentation says which. Choosing POST adds Body format: JSON or form fields.
  • Headers to send — the extra lines sent with the request. This is where an API key goes.
  • Values to send — the question you are asking. Fixed values, or answers from your own fields.
  • Values to use — which figures from the response the calculator can work with.
  • Reuse the answer for (seconds) — how long an answer is kept before the service is called again.

What headers are

A header is a labelled line sent alongside the request — not part of the address, and not visible in it. Services use them to know who is calling. The two you will meet most often:

Authorization    Bearer sk_live_7c2f9a...
X-API-Key        7c2f9a1b4e...

Which of them to use is the service’s decision, and their documentation will say. Add a row for each: the name on the left, the value on the right.

Your key never reaches the visitor. The call is made by our server, not by the browser, so the header is never in the page source and never in the network tab. That is the whole reason to put a key here rather than into a formula or a link.

What inputs to send

Values to send is how you ask a question rather than just asking for a list. A currency service needs to know which currencies; a delivery service needs the postcode the visitor typed.

Each row has the name the service expects and a value, and that value can be a fixed value or the answer from a field — so it changes with what the visitor fills in:

base       USD              a fixed value
symbols    Currency         the answer from a field
weight     Parcel weight    the answer from a field

For GET these are added to the address as a query string; for POST they are sent as the body, in the format you chose. You never have to build either by hand.

Reading the answer

Press Make a test call and the real response comes back into the panel. Say the service answers with this:

{
  "success": true,
  "base": "USD",
  "timestamp": 1756055321,
  "rates": {
    "gold_gram_24k": 74.35,
    "gold_ounce": 2312.40,
    "silver_gram": 0.92
  },
  "history": [
    { "day": "Mon", "close": 2298.10 },
    { "day": "Tue", "close": 2305.75 },
    { "day": "Wed", "close": 2312.40 }
  ]
}

The panel has two tabs. Choose response keys lists every value it found, each one clickable and searchable; clicking one adds it to Values to use. Raw response shows the same answer as formatted JSON, and the values there are clickable too — useful when you want to see the shape rather than a list.

A value inside something else is named after the path to it, so gold_gram_24k above arrives as rates_gold_gram_24k. Rename any of them to something shorter; the name is what formulas will use, and it is yours to choose. Pick the ones you need and ignore the rest.

Using the values

Anything you picked behaves like a field from then on. It appears in the Insert field list of the formula editor, in the rich text editor, and anywhere else a field can be inserted:

ROUND([rates_gold_gram_24k] * [grams], 2)

So a jeweller’s calculator multiplies the real price of gold by the weight somebody typed, and it is right again tomorrow without anybody editing it.

Charts and data tables from the response

Some responses carry a whole table rather than single figures. Make a test call and the connector offers the shapes it can see under Chart and table data. It understands two:

Rows of objects — like history above. Each entry is a row and each key is a column: day and close.

Lists side by side — several lists of the same length, one per column:

{
  "labels":  ["Mon", "Tue", "Wed"],
  "revenue": [1200, 1580, 1440],
  "cost":    [800, 910, 870]
}

Add the dataset here first. Then add a chart or a data table, and set its Data from to A dataset from an API Connector and pick the one you made. You then choose which column runs along the bottom and which are plotted, and you can rename any column for display — the name shown is a label, so renaming it never changes which data it points at.

The dataset has to exist before a chart can be pointed at it. If the chart says there are none yet, go back to the connector, make a test call, and add one.

Showing the response to visitors

The connector itself is hidden, because it is a source of values rather than something to look at. If you do want visitors to see what came back, its Design tab has Show the response as a table, with a heading of your own.

How often it calls

Reuse the answer for (seconds) decides how long one answer serves. At 0 — the setting a new connector starts with — the service is called afresh every time a visitor changes an answer. At 300 the answer is kept for five minutes, so a hundred visitors in that window cost one call rather than a hundred.

Most services charge by the call, or cut you off past a limit. A price that moves through the day is perfectly well served by a few minutes of cache, and the calculator answers faster for it. It is worth setting before you publish.

When it does not work

  • “The test call could not be made” — check the address is reachable and starts with https://. Private and internal addresses are refused on purpose.
  • An error from the service — the message is shown as they sent it. Usually a missing header, or a key that has expired.
  • Nothing in the list of keys — the answer was not JSON, or it was empty. The raw tab shows exactly what came back.
  • It works in the builder but not on the live page — some services only accept calls from addresses registered with them. Add your published domain to their allowed list.
  • A value stopped updating — it is being reused. Lower the seconds, or press Fetch the latest response in the panel to see the current answer.

Appointments and Calendly #

Two ways to take a booking:

  • Appointment — asks for a date and a time slot. You set which days and hours you offer, and how long each slot is, so only times you actually have can be chosen. The choice arrives in the lead email.
  • Calendly — embeds your Calendly page so visitors book without leaving the calculator. Paste your Calendly address into its settings.

Use the Appointment widget when the booking should arrive with the rest of the form. Use Calendly when your availability already lives there.

Charts and tables

Data tables and charts: showing rows and drawing figures, including schedules the calculator works out itself.

Data tables #

A Data table puts a grid on your page — rows going across, columns going down, like a small spreadsheet.

Use one when a single number is not the whole story. If a visitor asks “what will this cost?”, sometimes the honest answer is £4,200 — and sometimes it is a list showing where those £4,200 came from.

Tables are good for things like:

  • A quote broken down line by line, so people can see what they are paying for.
  • A year-by-year projection — savings, output, growth.
  • A payment schedule.
  • A price list or a comparison of your packages.

Adding one

In the left-hand panel, open Elements, find the Charts & tables group, and drag Data table onto your calculator.

Then, in the settings panel on the right, open the Data tab. This is where you tell the table what to put in it. Nothing will appear until you do.

Step one: where do the rows come from?

The Data from dropdown offers three choices. Pick the one that matches where your information already lives.

  • This calculator’s fields and results — the easiest. You tick which questions and answers to include, and the table lists them. Use this for a summary of what the visitor filled in. You can also flip it so each one becomes a column instead of a row.
  • A range from the connected Google Sheet — if you keep a price list in a spreadsheet, type where it sits, like A1:D20, and that block appears on your page. Tell it whether your first row contains the column headings.
  • Table Builder — for rows that do not exist yet. Nobody has typed them anywhere, because they depend on what the visitor just entered. You describe the rows once, and the calculator works them out. This is explained below.

There is also a switch called Reload when the visitor changes an input. Leave it on and the table keeps up as people type. The only reason to turn it off is a table that never changes, like a fixed price list.

Table Builder, in plain terms

The first two choices show information you already have. Table Builder is different: you write a short instruction, and the calculator builds the rows from it.

The instruction is called a TABLE() formula. It always starts the same way — your column headings in square brackets — and then you add rows underneath in one of two ways:

  • Type a row out yourself. Good when you know what the rows are and only the numbers change.
  • Use GENERATE(). Good when you do not know how many rows there will be, because that depends on the visitor’s answer — 5 years or 25, 12 months or 360.

You can use both in one table, and you can use one without ever touching the other.

About the three examples below. They are complete, working examples — not steps to follow in order, and not a set you need all of. Each one is a different way of using Table Builder. Find the one closest to what you are building, copy it into the Table box, and change the wording and the numbers to suit you. The names in square brackets like [guests] are your own fields, so swap those for yours.

Example #1 — An itemised quote (rows you type yourself)

A caterer wants to show what an event will cost, line by line. There are always the same four lines — only the quantities and totals move, depending on how many guests were entered.

TABLE(
  ["Item", "Quantity", "Each", "Line total"],
  ["Canapes on arrival",  [guests], 4.50, [guests] * 4.50],
  ["Three-course dinner", [guests], 32.00, [guests] * 32.00],
  ["Waiting staff",       [staff],  145,  [staff] * 145],
  ["Delivery and setup",  1,        180,  180]
)

With 80 guests and 4 staff, the visitor sees:

ItemQuantityEachLine total
Canapes on arrival804.50360.00
Three-course dinner8032.002,560.00
Waiting staff4145.00580.00
Delivery and setup1180.00180.00

Reading the formula:

  • The first line, ["Item", "Quantity", "Each", "Line total"], is the headings across the top.
  • Every line after it is one row of the table. Each gives four values, because there are four columns, in the same order.
  • Words go in quotes. Numbers do not.
  • [guests] means “whatever the visitor typed in the Guests field”, so [guests] * 4.50 is that number multiplied by 4.50.

To add a line, copy one and edit it. To remove a line, delete it. That is the whole idea — there is nothing else going on here.

Example #2 — A 25-year projection (rows the calculator makes)

A solar installer wants to show what a system saves over its lifetime. Typing out 25 rows would be miserable, and the visitor might choose 10 years or 30 — so instead you describe one row, and say how many to make.

TABLE(
  ["Year", "Output (kWh)", "Saving", "Saved so far"],
  GENERATE([years], ROW(
    INDEX,
    IF(INDEX = 1, [system_kw] * 950, PREV("Output (kWh)") * 0.995),
    CURRENT("Output (kWh)") * [tariff],
    IF(INDEX = 1, CURRENT("Saving"), PREV("Saved so far") + CURRENT("Saving"))
  )),
  ["Total", "", "", PREV("Saved so far")]
)

The visitor sees this (shortened — it really runs to 25 rows):

YearOutput (kWh)SavingSaved so far
14,275.001,026.001,026.00
24,253.631,020.872,046.87
34,232.361,015.773,062.64
Total24,168.41

GENERATE([years], ROW(...)) means “make this many rows, and here is what one row looks like”. Inside it, three special words let a row know about the rest of the table:

  • INDEX — which row this is, counting 1, 2, 3… Above, it fills the Year column, and it is also how the first row is told apart from the others.
  • PREV("Output (kWh)") — the value in that column on the row above this one. That is how each year comes out slightly lower than the last: it is 99.5% of whatever the year before was.
  • CURRENT("Saving") — a value already worked out on this same row, in a column further left. Columns fill in left to right, so you can only look leftwards.

You will see IF(INDEX = 1, ..., ...) twice. It means “if this is the very first row, use this; otherwise use that”. The first row needs it because there is no row above it to look at.

The last line is worth noticing. A typed row placed after a GENERATE() becomes a total at the bottom, because PREV() there reads the final row the calculator made. Put a typed row before the GENERATE() instead and it becomes an opening balance at the top.

Example #3 — A repayment schedule (each row builds on the last)

This is Example #2’s idea pushed a little further. Every row depends on what was left over by the row above it, which is what makes a loan schedule work.

TABLE(
  ["Month", "Payment", "Interest", "Balance"],
  GENERATE([term_months], ROW(
    INDEX,
    ABS(PMT([rate] / 12, [term_months], [amount])),
    IF(INDEX = 1, [amount], PREV("Balance")) * [rate] / 12,
    IF(INDEX = 1, [amount], PREV("Balance"))
      - (CURRENT("Payment") - CURRENT("Interest"))
  )))

On £10,000 over 12 months at 6%:

MonthPaymentInterestBalance
1860.6650.009,189.34
2860.6645.958,374.62
3860.6641.877,555.83
12860.664.280.00
PMT() hands back a negative number — that is the spreadsheet convention for money going out. So it is wrapped in ABS(), which strips the minus sign off. Forget that and the balance climbs instead of falling, which looks like a broken calculator.

This same shape covers far more than loans. Anything with a running total works the same way: equipment losing value year by year, stock counting down week by week, holiday days accrued and taken, a project spending through its budget. Only the arithmetic in the middle changes.

Tidying up the numbers

Each column can be formatted on its own — as money, as a percentage, or as a plain whole number. That is how a Year column stays 1, 2, 3 while the column next to it reads £1,026.00.

Do not go mad with row counts. A 30-year monthly schedule is 360 rows, which is already more than anybody reads. If you find yourself making thousands, switch to yearly rows instead — it will be quicker and far easier to look at.

Making the table easier to read

Open the Design tab once there is real data in your table. These are the settings that make the difference:

  • Let visitors sort columns — they can click a heading to reorder the rows.
  • Show a search box — filters the rows as they type. Worth turning on past about ten rows.
  • Keep the header visible when scrolling — the column headings stay put on a long table, so people do not lose track of which column is which.
  • Shade alternate rows — on already, and it helps more than anything else here. Leave it on.
  • Maximum height — set a height and the rows scroll inside the table, instead of a long table shoving the rest of your page down the screen. Almost always what you want for a schedule.
  • Text alignment — choose Labels left, numbers right. It lines the digits up underneath each other, and columns of money are very hard to compare otherwise.

If your table is wide, set the width to Fixed maximum. It will then scroll sideways rather than squashing the columns, which is what you want on a phone.

If your table looks wrong

  • It is empty — you probably have not chosen anything in the Data tab yet, or the results it shows have not been calculated. Preview the calculator and fill the form in.
  • “a row has 3 values but there are 4 columns” — you have one heading more, or fewer, than the values in a row. Count both and make them match.
  • Everything shows as 0 — a field name in square brackets probably does not match a real field. Check the spelling.

Charts #

The Chart element draws your figures instead of listing them. It is the right choice when the point is a comparison or a shape — which option costs less, how a balance falls over time, what the split is — and the wrong choice for a single number, which is just a result.

Add it from Elements › Charts & tables in the left-hand panel, then pick a chart type.

Choosing a chart type

  • Bar — comparing a handful of things. The safe default, and the easiest to read.
  • Stacked bar — comparing totals while also showing what each total is made of.
  • Line and Area — something changing over time. A balance, a projection, growth.
  • Pie and Donut — parts of one whole, and only when there are a few of them. Past about five slices nobody can tell which is bigger.
  • Progress / radial and Gauge — one value against a target or a maximum. A score, a percentage, a capacity.
  • Combo — bars and a line together, for two things on different scales: monthly cost as bars, cumulative total as a line.
  • Scatter — the relationship between two numbers across many points.

Where the figures come from

The same three sources a data table uses:

  • This calculator’s fields and results — choose which ones to plot. A bar per result is the commonest chart in a calculator.
  • A range from the connected Google Sheet — give the range, then say which column holds the labels and which columns to plot. Both are counted from 0, so 1,2 plots the second and third columns as two series. Leave the columns blank to plot every column except the labels.
  • Table Builder — plot the schedule you generated. The same rows can feed a table and a chart, so a visitor gets the shape and the detail together.

Appearance

  • Height — in pixels. The width always follows the column it sits in.
  • Colors — follow the calculator theme, a distinct color per series, or one color from light to dark. You can also pick colors yourself: on a pie or donut those color the slices, on anything else the series. Leave an entry blank and it falls back to the palette, so coloring the third series does not mean choosing the first two by hand.
  • Show a legend — appears automatically once there is more than one series.
  • Label each value — prints the figure on the bar or point. Good on a short chart, cluttered on a long one.
  • Show gridlines — the faint lines behind the plot.
  • Figures up the side — how the scale is written: as calculated, a plain number, a whole number, currency or a percentage. Without it an axis of money reads 25000 beside a result saying £25,000.00, which is the same figure written two ways on one page.
That last setting affects the scale up the side only. The label on each value keeps the format its own figure already carries, so nothing is formatted twice.

A chart with nothing in it

An empty chart nearly always means one of three things: the results it plots have not been calculated yet, the fields chosen produce text rather than numbers, or a sheet range points at the wrong worksheet. Preview the calculator and fill the form in — a chart of results has nothing to draw until there are results.

Formulas and results

Referencing fields, the function library, conditional logic and generated tables.

Writing a formula #

A result can be worked out here with the formula builder, read from a Google Sheet cell, or built from both — see three ways to calculate. This section covers the formula builder.

Add a Result element, open its settings, and write a formula. Refer to any field by its key in square brackets.

[loan_amount] * [interest_rate] / 100

Arithmetic works as you would expect — + - * / — and brackets control the order:

([room_width] * [room_length]) * [price_per_sqm]

A monthly total from an annual figure, with a call-out fee added:

([annual_cost] / 12) + [call_out_fee]

Results can be used inside other results, so a long calculation can be built in readable steps:

[subtotal] * (1 + [tax_rate] / 100)
Use the field picker rather than typing keys by hand. It inserts the right key and cannot misspell one — a mistyped key is the commonest reason a result stays blank.

A result shows a dash until every field it needs has a value. That is the calculator waiting for an answer, not an error.

Function reference #

Every function below can be used in any formula. The examples use field keys, so they read the way a real formula does.

Conditions

IF(condition, value_if_true, value_if_false)

Checks a condition and gives one value when it is true and another when it is false.

conditionA test, such as [total] > 500. Either true or false.
value_if_trueWhat to use when the test passes.
value_if_falseWhat to use when it does not.

Example. Free delivery over 500, otherwise a flat fee.

IF([order_total] > 500, 0, 4.95)
AND(condition1, condition2, ...)

True only when every condition is true. Use it inside IF().

condition1, condition2, ...Every test that must pass. Use it inside IF().

Example. A weekend surcharge, but only on jobs over four hours.

IF(AND([is_weekend] = 1, [hours] > 4), [subtotal] * 1.25, [subtotal])
OR(condition1, condition2, ...)

True when at least one condition is true. Use it inside IF().

condition1, condition2, ...The tests, any one of which is enough. Use it inside IF().

Example. An access surcharge for either awkward case.

IF(OR([floor_level] > 2, [no_lift] = 1), [subtotal] + 60, [subtotal])
NOT(condition)

Reverses a condition: true becomes false and false becomes true.

conditionThe test to reverse.

Example. A booking fee for everyone except members.

IF(NOT([is_member] = 1), 25, 0)

Dates

TODAY()

Today's date. Add a number to it for a date in the future: TODAY() + 30 is thirty days from now.

Example. How long until the date the visitor chose.

DAYS([event_date], TODAY())
DATE(year, month, day)

Builds a date from a year, a month and a day. A month past 12 rolls into the next year, as in a spreadsheet.

yearThe four-digit year.
monthThe month, 1 to 12.
dayThe day of the month.

Example. Build a fixed date to count towards.

DAYS(DATE(2026, 12, 25), TODAY())
EDATE(start_date, months)

The same day a number of months later, or earlier if months is negative. Where that day does not exist it lands on the last day of the month, so a payment due on the 31st falls on the 28th in February.

start_dateThe date to count from.
monthsHow many months to add. A negative number counts backwards.

Example. When a policy taken out today comes up for renewal.

EDATE(TODAY(), [term_months])
DAYS(end_date, start_date)

How many days from one date to another. The same as subtracting them, and clearer to read.

end_dateThe later date.
start_dateThe earlier date.

Example. Equipment hire charged by the day.

DAYS([return_date], [collection_date]) * [daily_rate]
DATEDIF(start_date, end_date, unit)

Whole years, months or days between two dates. The unit is "Y", "M" or "D" in quotes. This is how ages are worked out: YEAR() subtraction is wrong for anyone whose birthday has not come round yet.

start_dateThe earlier date.
end_dateThe later date.
unitWhat to count in: "d" for days, "m" for months, "y" for years.

Example. Somebody age, for an insurance quote.

DATEDIF([date_of_birth], TODAY(), "y")
YEAR(date)

The year of a date, as a number.

dateThe date to read the year from.

Example. A surcharge for vehicles over ten years old.

IF(YEAR(TODAY()) - YEAR([registered_on]) > 10, [premium] * 1.2, [premium])
MONTH(date)

The month of a date, 1 to 12.

dateThe date to read the month from, 1 to 12.

Example. Peak-season pricing for summer bookings.

IF(AND(MONTH([booking_date]) >= 6, MONTH([booking_date]) <= 8), [rate] * 1.3, [rate])
DAY(date)

The day of the month of a date, 1 to 31.

dateThe date to read the day of the month from.

Example. Which day of the month a billing cycle starts on.

DAY([start_date])
WEEKDAY(date)

Which day of the week a date falls on: 1 is Sunday, 7 is Saturday.

dateThe date to read. Gives 1 for Sunday through to 7 for Saturday.

Example. A weekend surcharge worked out from the date itself, so nobody has to tick a box.

IF(OR(WEEKDAY([job_date]) = 1, WEEKDAY([job_date]) = 7), [subtotal] * 1.5, [subtotal])
DATETEXT(date)

Writes a date out as text, in the calculator's date format. Use it where a date has to be read rather than counted - a column of a table, for instance.

Finance

PMT(rate, nper, pv, fv, type)

The payment for a loan or investment, per period. Negative when pv is money you received, so wrap it in ABS() to display it.

rateThe interest rate PER PERIOD. A yearly rate paid monthly must be divided by 12.
nperThe total number of payments. 25 years paid monthly is 300.
pvThe present value: the amount borrowed.
fv optionalThe balance left at the end. Usually 0.
type optional0 when payments fall at the end of each period (the usual case), 1 at the start.

Example. The monthly payment on a mortgage, from an amount, a yearly rate and a term in years.

ABS(PMT([interest_rate] / 100 / 12, [term_years] * 12, [loan_amount]))
IPMT(rate, per, nper, pv, fv, type)

The interest part of the payment for one period. "per" counts from 1 and must not be more than nper.

rateThe interest rate per period.
perWhich payment you are asking about, counting from 1.
nperThe total number of payments.
pvThe amount borrowed.
fv optionalThe balance left at the end.
type optional0 for payments at the end of a period, 1 for the start.

Example. How much of the very first payment is interest rather than repayment.

ABS(IPMT([interest_rate] / 100 / 12, 1, [term_years] * 12, [loan_amount]))
PPMT(rate, per, nper, pv, fv, type)

The principal part of the payment for one period. IPMT() and PPMT() for the same period add up to PMT().

rateThe interest rate per period.
perWhich payment you are asking about, counting from 1.
nperThe total number of payments.
pvThe amount borrowed.
fv optionalThe balance left at the end.
type optional0 for payments at the end of a period, 1 for the start.

Example. How much of the first payment actually reduces the debt.

ABS(PPMT([interest_rate] / 100 / 12, 1, [term_years] * 12, [loan_amount]))
PV(rate, nper, pmt, fv, type)

What a series of future payments is worth today.

rateThe rate per period.
nperThe number of periods.
pmtThe payment made each period.
fv optionalThe value at the end.
type optional0 for the end of a period, 1 for the start.

Example. How much somebody can borrow, given what they can afford each month.

ABS(PV([interest_rate] / 100 / 12, [term_years] * 12, -[monthly_budget]))
FV(rate, nper, pmt, pv, type)

What regular savings grow to. Money you pay in is negative.

rateThe rate per period.
nperThe number of periods.
pmtThe amount paid in each period.
pv optionalThe amount started with.
type optional0 for the end of a period, 1 for the start.

Example. What a savings pot is worth after years of regular contributions.

ABS(FV([rate] / 100 / 12, [years] * 12, -[monthly_saving], -[opening_balance]))
NPV(rate, value1, value2, ...)

Net present value of cash flows, the first one a full period away. For money spent today, add it outside: NPV(rate, ...) + [cost_today].

rateThe discount rate per period.
value1, value2, ...The cash flows, in order, one per period.

Example. Whether an investment is worth making, valued in today money.

ROUND(NPV([discount_rate] / 100, [year_1], [year_2], [year_3]) - [initial_cost], 2)
IRR(value1, value2, ...)

The return that makes a set of cash flows break even. The first value is today and is normally negative. Returns a rate, so x100 for a percentage.

value1, value2, ...The cash flows, starting with the initial outlay as a negative number.

Example. The effective annual return on an investment.

ROUND(IRR(-[initial_cost], [year_1], [year_2], [year_3]) * 100, 2)
RATE(nper, pmt, pv, fv, type, guess)

The interest rate per period of a loan or investment. Multiply by the periods in a year for the yearly rate.

nperThe number of periods.
pmtThe payment per period.
pvThe present value.
fv optionalThe value at the end.
type optional0 for the end of a period, 1 for the start.
guess optionalA starting estimate, for the rare case the usual one does not settle.

Example. The yearly rate implied by a payment somebody has been quoted.

ROUND(RATE([term_years] * 12, -[monthly_payment], [loan_amount]) * 12 * 100, 2)
NPER(rate, pmt, pv, fv, type)

How many periods it takes to pay off or reach an amount.

rateThe rate per period.
pmtThe payment per period.
pvThe present value.
fv optionalThe value at the end.
type optional0 for the end of a period, 1 for the start.

Example. How many months it takes to clear a balance at a given payment.

CEIL(NPER([apr] / 100 / 12, -[monthly_payment], [balance]))

Lookup

LOOKUP(table, value, value_if_not_found)

Finds a value in one of your lookup tables and returns the matching amount. Add a third value to use when nothing matches, otherwise a missing row is an error.

tableThe lookup table, by name, from Options then Variables and tables.
valueThe value to find in the table left-hand column.
value_if_not_found optionalWhat to use when nothing matches.

Example. A price band chosen by the service level the visitor picked.

LOOKUP([pricing], [service_level], 0) * [quantity]

Maths

SUM(value1, value2, ...)

Adds every value together.

value1, value2, ...Any number of values or fields to add together.

Example. A quote made of separate line items.

SUM([materials], [labour], [delivery], [permit_fee])
MIN(value1, value2, ...)

Gives the smallest of the values.

value1, value2, ...The values to compare.

Example. Cap a discount so it can never exceed 200, however large the order.

MIN([order_total] * 0.15, 200)
MAX(value1, value2, ...)

Gives the largest of the values.

value1, value2, ...The values to compare.

Example. Charge a minimum call-out fee, even for a tiny job.

MAX([hours] * [hourly_rate], 95)
AVG(value1, value2, ...)

Gives the average (mean) of the values.

value1, value2, ...The values to average.

Example. An overall score from three rated answers.

ROUND(AVG([quality], [speed], [value_for_money]), 1)
ROUND(value, decimal_places)

Rounds a value to the given number of decimal places (0 if not given).

valueThe number to round.
decimal_placesHow many digits after the point. 0 gives a whole number, 2 gives pence.

Example. A quote that reads as money rather than as arithmetic.

ROUND([subtotal] * 1.20, 2)
CEIL(value)

Rounds up to the next whole number.

valueThe number to round up to the next whole number.

Example. Materials sold whole: 4.2 tins of paint means buying 5.

CEIL([wall_area] / [coverage_per_tin])
FLOOR(value)

Rounds down to the previous whole number.

valueThe number to round down to the previous whole number.

Example. How many whole units fit in the space available.

FLOOR([room_length] / [unit_width])
ABS(value)

Removes a minus sign, giving the size of a number.

valueThe number to strip the sign from.

Example. Show a difference as a size, whichever way round it came out.

ABS([current_bill] - [projected_bill])
POW(value, power)

Raises a value to a power. POW(2, 10) is 2 multiplied by itself 10 times.

valueThe base number.
powerThe exponent to raise it to.

Example. Compound growth over a number of years.

ROUND([deposit] * POW(1 + [rate] / 100, [years]), 2)
SQRT(value)

Square root of a value.

valueThe number to take the square root of. Must not be negative.

Example. The side length of a square area.

ROUND(SQRT([floor_area]), 2)
MOD(value, divisor)

The remainder left after dividing.

valueThe number to divide.
divisorWhat to divide it by.

Example. What is left over after filling whole boxes.

MOD([total_items], [items_per_box])

Text

CONCAT(value1, value2, ...)

Joins values together into one piece of text. Numbers are written as they are, so put any rounding or currency symbol in yourself.

value1, value2, ...Any number of pieces to join, in the order they should read. Text goes in double quotes; fields and numbers go in as they are.

Example. An estimate shown as a range rather than as two separate figures.

CONCAT("$", ROUND([estimate_low], 0), " - $", ROUND([estimate_high], 0))
VALUE(text)

Reads a number out of text - VALUE("1,250.50") is 1250.5. Rarely needed: a field holding "12" is already read as 12 in any sum. Text with no number in it is 0.

FORMAT(value, format_code, decimal_places)

Writes a number out the way the calculator writes its results - thousands separators, currency symbol, per-cent sign and all. The code is number, currency or percentage. Decimal places are optional: without them the calculator's own setting is used.

valueThe number to write out. A field, a variable or a calculation - anything that answers with a number.
format_codeHow to write it, in double quotes: "number" for thousands separators, "currency" for the calculator's own currency symbol, "percentage" for a per-cent sign.
decimal_places optionalHow many decimal places to show. Left out, the calculator's own setting is used - which is usually what you want, since it is the setting every result already follows.

Example. A figure written into a sentence, so it reads the way the same figure reads in a result field above it.

FORMAT([total], "currency")        gives  $1,250.50
FORMAT([total], "currency", 0)     gives  $1,251
FORMAT([rate], "percentage", 1)    gives  7.3%
FORMAT([units], "number", 0)       gives  1,251

CONCAT("You save ", FORMAT([saving], "currency"), " a year")

Worked examples #

A minimum charge. Never quote below £150, whatever the size:

MAX([area] * [rate], 150)

A discount over a threshold. 10% off orders above 500:

IF([order_total] > 500, [order_total] * 0.9, [order_total])

Two conditions at once. A weekend surcharge for long jobs:

IF(AND([is_weekend] = 1, [hours] > 4), [subtotal] * 1.25, [subtotal])

Rounded to whole pounds, so a quote does not read £1,284.3199:

ROUND([subtotal] + [delivery], 0)

A price band from a lookup table rather than a chain of IFs:

LOOKUP([pricing], [service_level]) * [quantity]

Distance-based delivery, using the drive distance widget:

ROUND([drive_distance] * 1.20 + [base_fee], 2)

An estimate as a range, joined into one line with CONCAT. Show it with a Result (text) element:

CONCAT("$", ROUND([estimate_low], 0), " - $", ROUND([estimate_high], 0))

A sentence with a figure in it, for a summary line:

CONCAT("Your monthly payment is $", ROUND([monthly], 2), " over ", [years], " years.")

Variables #

A variable is a constant you name once and use everywhere — a tax rate, a call-out fee, a markup.

  1. In the builder, open Options › Variables & tables.
  2. Press Add variable.
  3. Name it tax_rate and set the value to 8.25.
  4. Use it like a field.
[subtotal] * (1 + [tax_rate] / 100)

Change the variable and every formula using it updates — which beats hunting for a number typed into six places.

Lookup tables #

A lookup table maps a value to a number, so a choice can set a price without a long chain of IF statements.

  1. Open Options › Variables & tables.
  2. Press Add lookup table and name it, for example pricing.
  3. Add rows: the value to match on the left, the number to use on the right.
  4. Read it in a formula.
LOOKUP([pricing], [service_level])

The left column must match the value of the choice, not its label. If your dropdown option reads “Premium finish” but its value is premium, the table needs premium.

To load a long table, press Upload a CSV. The file must have exactly two columns; Download template gives you the shape:

When value is,Use
Basic,50
Standard,95
Premium,140

Showing results #

  • Result — the number your formula works out. Give it a label, a prefix or suffix, and decimal places.
  • Result paragraph — a sentence with results written into it, so the answer reads as an explanation rather than a bare figure.
  • Editable result — a result the visitor can overwrite. Use it where your figure is a starting estimate they may know better than you.

Number formatting — separators, decimal places, currency symbol and its position — is set once for the whole calculator under Options › Calculator settings.

Google features

Google Sheets or an Excel workbook as the calculation engine, plus Maps, addresses, distance and reviews.

Connecting a Google Sheet #

A calculator can do its arithmetic in a Google Sheet you already maintain. EmbedSheets writes the visitor’s answers into the cells you nominate, lets the sheet recalculate, and reads the results back.

The sheet must be shared with edit access. Read-only is not enough: the calculator has to write the visitor’s answers into it before it can read anything back.
  1. Open your Google Sheet.
  2. Press Share in the top right.
  3. Add this address as an Editor:
    embedsheets@googlegroups.com
  4. Copy the sheet address from your browser.
  5. In the builder, open Options › Connect Google Sheet.
  6. Paste the address and connect.

Then map your fields and results to cells: choose the worksheet and give a cell reference such as B4. Inputs are written in; results are read out.

The same connection can also collect submissions — see saving submissions to your sheet.

Common problems

  • “We cannot open that sheet” — the address above has not been added as an Editor. Viewer access will not work.
  • Results come back empty — check the worksheet, not just the cell. B4 exists on every tab.
  • The number is stale — make sure the result cell is a formula that depends on the input cells, not a value typed in.
  • Two visitors, mixed-up numbers — cannot happen. Each calculation takes a lock, so requests queue rather than overlap.

Saving submissions to your sheet #

A calculator connected to a spreadsheet can also write into one. Every submission is appended as a row: who the person is, what they answered, and what came out.

Open Options › Connect Google Sheet and, under Submissions, switch on Save leads to Google Sheets and choose the tab to add them to.

Choose a different tab from the calculation one. The calculation tab has each visitor’s answers written into its input cells and read back again, so anything kept there is overwritten by the next person who opens the form. The dialog offers every tab in the spreadsheet; pick an empty one, or make a new one called something like Submissions.

What a row looks like

The first four columns are always the same. After them come your input fields in the order they appear on the form, and then your results:

Submitted            Name        Email              Phone        Room type  Room size  Estimate
2026-08-24 16:55:41  Dana Reed   dana@example.com   0161 496...  Kitchen    240        $18,400

An empty tab gets that heading row written first, so the sheet reads properly from the start. A tab you have already arranged is left alone — rows are added to the end and nothing above them is touched.

Things worth knowing

  • It is in addition to Leads, not instead of it. Every submission is still stored in your account, still emailed, still sent to your webhook. The sheet is a copy for you to work in.
  • Everything is written as text. A phone number keeps its leading zero and a value beginning with = stays a value rather than becoming a formula in your spreadsheet.
  • A form with no contact fields still works. The name, email and phone columns are there and simply empty, so every row has the same shape.
  • Rows are appended, never overwritten. Two people submitting at the same moment cannot land on the same row.
  • It happens just after the visitor is thanked, not while they wait — writing to Google takes a moment and it is not their moment to spend.

When a row does not appear

  • The spreadsheet must still be shared with embedsheets@googlegroups.com as an Editor. Viewer access can read but not write.
  • The tab must still exist. If it was renamed or deleted, choose it again.
  • Adding a column to your form adds a column to the row. Existing rows keep the headings they were written with, so it is worth deciding the fields before you start collecting in earnest.

Using an Excel spreadsheet #

If your pricing or your model already lives in Excel, you do not have to rebuild it. EmbedSheets talks to Google Sheets rather than to Excel directly, so the job is to get your workbook into Google Sheets once — after that it behaves exactly like any other connected sheet.

It takes about two minutes, and you keep your original .xlsx file untouched.

Step 1 — put the file in Google Drive

  • Go to drive.google.com and sign in.
  • Drag your .xlsx (or .xls) file into the window, or use New › File upload.
  • Wait for it to finish uploading.

Step 2 — save it as a Google Sheet

An uploaded Excel file is still an Excel file. Google can open it, but EmbedSheets cannot read from it until it has been converted.

  • Double-click the uploaded file in Drive to open it.
  • In the menu, choose File › Save as Google Sheets.
  • A new file appears, with the same name and a green spreadsheet icon. This new one is the file you will connect.
You now have two files: the Excel original and the Google Sheet copy. They are not linked. Editing the .xlsx will not change the copy, so from here on, make your changes in the Google Sheet.

Step 3 — share it with EmbedSheets

  • With the new Google Sheet open, press Share in the top right.
  • Add this address and set it to Editor:
    embedsheets@googlegroups.com
  • Press Send. There is nobody to notify — it is not a person — so you can untick the notification box.

Editor access is required, not optional. The calculator has to write each visitor’s answers into your input cells before the sheet can work anything out, and a viewer cannot write.

Step 4 — connect it and map your cells

  • Copy the sheet’s address from your browser’s address bar.
  • In the builder, open Options › Connect Google Sheet and paste it in.
  • Point each of your fields at the cell it should fill in — the cells you would normally type into by hand.
  • Point each result at the cell that holds the answer.

The rest is covered in Connecting a Google Sheet, which applies without change once the conversion is done.

What survives the conversion, and what does not

Ordinary spreadsheet models come across intact. The things that do not are worth knowing before you start:

  • Formulas — nearly all of them convert. SUM, IF, VLOOKUP, INDEX/MATCH, the financial functions and the date functions all have Google equivalents with the same names.
  • Multiple worksheets — kept, and you can point cells at any of them. Give the worksheet as well as the cell reference: B4 exists on every tab.
  • Named ranges and cell formatting — kept.
  • Macros and VBAnot kept. Google Sheets does not run VBA. If a number depends on a macro, it will not update.
  • Pivot tables and charts — usually convert, but check them. You will not need the charts anyway: build those in the calculator with the Chart element, where they can react to what the visitor typed.
  • Power Query and external links — not kept. Anything pulling from another workbook or a database has to be replaced with values, or rebuilt.

Open the converted sheet and look for #NAME? or #REF! before you connect it. Those are the two errors a conversion leaves behind, and a calculator built on top of one will return errors too.

Do I have to use a spreadsheet at all?

No. A sheet is worth it when the model already exists, when somebody who is not you maintains the pricing, or when the maths is genuinely large. For a calculator you are writing from scratch, the built-in formula builder is faster and there is nothing to keep in sync — see Three ways to calculate.

Address lookup and autocomplete #

An Address field can suggest real addresses as the visitor types. Open the field’s settings and switch on Google address lookup.

This is required before an address field can be used for distance or maps. Those need a resolved place, not typed text.

Drive distance and time #

Google Distance measures the driving distance and time between two address fields and gives you both as numbers.

  1. Add two Address fields and switch on Google address lookup in each.
  2. Add the Google Distance widget.
  3. Choose the From and To address fields in its settings.
  4. Pick miles or kilometres.

It is normally hidden from visitors — it is a value the calculator works out, not a question. Use it in a formula:

[drive_distance] * 2.50 + [call_out_fee]

Switch on the drive time as well and a second value becomes available, in minutes or seconds:

[drive_distance_time] * [hourly_rate] / 60

Google Maps #

The Google Maps widget shows a fixed address, or the addresses your visitors type.

  • One address you type here — the same map for everyone.
  • Addresses the visitor enters — choose which address fields to plot, in the order the journey visits them.

With several addresses, choose how to draw them: a route from first to last following real roads, or a marker on each.

Map type offers Map, Satellite, Satellite with labels and Terrain. The last two need the still-picture map, so a route shows the closest match Google offers.

Google Reviews #

Shows your real Google reviews and star rating.

  1. Add the Google Reviews widget.
  2. In its settings, start typing your business name or address.
  3. Pick your business from the suggestions.

Choose a scrolling, stacked or grid layout, and a light or dark theme. You can add a “View all reviews” link to your Google business page.

Reviews are cached for a week to keep API use down, so a brand new review may take a few days to appear.

The Options menu

Number formats, theming, domains and the settings that apply to a whole calculator.

Theme: colours and styling #

Options › Theme controls how the calculator looks. Start from a preset — Light, Dark or Black — then adjust anything.

The quickest route is Theme color. Set your brand colour and it updates the primary and accent colours, focus borders, buttons, their hover states, checkbox ticks and the quieter tinted borders in one go. Anything you then change by hand overrides it.

Beyond that you can set every colour individually: backgrounds, headings, body text, labels, input fields, buttons and result panels, plus corner radius and border width.

Background can be solid or transparent, which lets the calculator sit directly on your own page background rather than in a white box.

A live preview beside the settings shows real calculator markup, and a readability warning appears if a combination would be hard to read.

Font

Font sets the typeface for the whole calculator, from a short list of Google Fonts. It is loaded on the published page only.

The default is the visitor’s own system font, which is already on their computer and needs no download at all. Choose a Google font when the calculator has to match the type on the page around it — not by default.

Width, corners and the card

The Layout group decides the shape of the calculator rather than its colours. It is also where Show as a card and Theme color sit.

  • Maximum width (desktop) — 320px to 1920px. A ceiling, not a width: below it the calculator fills whatever it is given, so this only takes effect on a screen wide enough to reach it. Lower it when a calculator looks stretched across a wide page.
  • Calculator corners — 0px to 40px, the rounding of the calculator’s own outer edge.
  • Card corner radius and Card border — used when the card style is switched on.

Card or no card

Show as a card gives the calculator a border and a soft shadow so it sits on your page as a distinct panel. Off, it blends with whatever surrounds it, which is usually what an embedded calculator wants.

A card cannot be combined with a transparent background. A card is a border around a fill, and transparent is the absence of one, so switching the background to transparent turns the card off.

Fitting into the page it is embedded on

Three settings do most of the work of making a calculator look like part of somebody’s site rather than a thing dropped onto it:

  • Transparent background, so your own page colour shows through.
  • Maximum width, to stop it stretching across a wide content column.
  • Theme color set to the site’s brand colour.

The embed snippet has a maximum width of its own as well — see Publishing and getting the embed code. That one limits the frame the calculator sits in; this one limits the calculator inside it.

Running a row to the edges

A calculator has padding so its fields do not touch its border. One row can opt out: open the row’s settings and switch on Run to the edges under Advanced. It is meant for a header image or a banner, which reads as a picture placed inside a form if it has a gap down each side.

Number, currency and date formats #

Options › Calculator settings decides how every number in the calculator is written.

  • Number format — United States / UK (1,234.56), European (1.234,56), or a custom pair of separators.
  • Decimal places — how many digits after the point.
  • Currency symbol, and whether it goes before ($1,234) or after (1,234$) the number.
  • Percentage and negative styles.
  • Large amounts — whether to shorten them.

These apply to every result at once, so you set them here rather than on each result element.

When results update #

In Options › Calculator settings, under Calculation behaviour, is Recalculate as the visitor types. The same switch is on the settings panel of any Calculate button.

  • On — results update with every keystroke. Best for short calculators where the answer is the point.
  • Off — results only update when a Calculate button is pressed. Better for long forms, and for calculations that read a Google Sheet.

Integrations: webhooks and analytics #

Options › Integrations holds two things.

Webhooks

Every submission can be posted to a webhook the moment a visitor submits. Any CRM that accepts an incoming webhook, or a tool such as Zapier or Make, can take it from there. Paste the address your CRM gave you.

See the webhook guide for the format.

Google Analytics

Add your measurement ID and the calculator reports views and submissions to your existing Analytics property, so calculator activity sits alongside the rest of your site traffic.

Meta Pixel

Add your Pixel ID — the number from Events Manager, not the whole snippet — and the published calculator fires a PageView against your pixel. We build the code, so there is nothing to paste.

An embedded calculator is an iframe, so its pixel is separate from the one on the page around it. Yours sees the calculator; theirs sees their page. Neither of them runs inside this builder.

Leads, emails and PDFs

Capturing enquiries, the emails they trigger, and turning results into a PDF.

Collecting leads #

Options › Leads & emails decides what happens when somebody submits.

  • Admin email — who receives each lead.
  • Send a copy to the visitor — choose which field holds their email address, and they get their own results.
  • Success message — what they see after submitting.
  • Hide results until contact details are given — the visitor must complete the fields you choose before results appear.

Two ways to use it:

  1. Contact first — they complete the calculator, submit their details, and the results are revealed.
  2. Results first — results show immediately, with a contact form underneath for anyone who wants to get in touch.

Every lead is listed under View Leads, with its inputs, results and any attachments, and can be exported as a CSV.

Email templates and your own SMTP #

In the same panel you can rewrite both emails — the one to you and the one to the visitor — setting the subject and body of each, and switching either off.

By default mail is sent from EmbedSheets. To send from your own address, switch on custom SMTP and give your host, port, username, password, encryption, and the from name and address.

Sending from your own domain usually improves delivery, because the address people see matches the domain that sent it.

PDF results #

A calculator can hand the visitor a PDF of their results. Open Options › Connect PDF Template and choose one of two sources.

The built-in template

The default, and it needs no setup at all. It lays out the visitor’s answers and results as a clean printable page. You can dress it with:

  • Your logo
  • A heading
  • Footer text — terms, a validity note, contact details
  • The file name it saves as

Your own Google Doc

For a fully designed quote or report, use a Google Doc instead.

  1. Create a Google Doc and design your document.
  2. Where a value should appear, write the field key in curly braces: {{customer_name}}, {{total_cost}}.
  3. Share the doc with Anyone with the link as Viewer.
  4. In Options › Connect PDF Template, choose the Google Doc source and paste the address.

Either way, add a Button element and set its action to produce the PDF.

Common problems

  • Placeholders come out literally — the key inside the braces does not match a field key. Check it under the field’s Advanced tab.
  • “We cannot open that document” — sharing is still restricted.
  • Formatting looks wrong — a Google Doc keeps its own formatting. Style it in Google Docs rather than here.

Publishing and embedding

Embedding on any website, share links, and what visitors can and cannot see.

Publishing and getting the embed code #

Press Publish in the builder’s top bar. The panel gives you two ways to put the calculator in front of people.

Embed on your website

Copy the snippet and paste it into your page where the calculator should appear. It resizes itself as the visitor moves through the calculator, so you do not need to guess a height.

Set a maximum width in pixels if you want to stop it stretching across a wide page.

Pasting it into WordPress, Shopify, Wix, Squarespace or Webflow

The snippet is ordinary HTML, so it goes wherever a page builder lets you put HTML. What that block is called is the only thing that differs.

  • WordPress — add a Custom HTML block in the block editor, or switch to the Text tab in the classic editor. Do not paste it into the Visual tab, which escapes the tags and prints the snippet as words.
  • Shopify — in a page, use the </> button in the rich text toolbar to switch to HTML. In a theme, add a Custom Liquid section.
  • Wix — Add › Embed Code › Embed HTML, then choose Code rather than Website address.
  • Squarespace — add a Code block and leave it set to HTML.
  • Webflow — drag in an Embed element.

Everywhere else, the rule is the same: an HTML or code block, never a rich text box.

Nothing to install — there is no plugin or app for any of these. The snippet is the whole integration, and a calculator you republish updates everywhere it is pasted without you touching those pages again.

Shareable link

A full-page version you can send to anyone — useful in an email, a social post, or a QR code. Open it shows you exactly what recipients will see.

Every calculator has one from the moment it is created:

  • esforms.com/1756055321847 — its key. This never changes, whatever else you do, so a link already sent out cannot be broken.
  • esforms.com/quote — a name you choose, if nobody has it yet. First come, first served.
  • esforms.com/acme/quote — the same name inside a word of your own, so you can have the short name you wanted even when somebody else took it at the top level.

Set the name under Share › Choose a name for this link. It tells you as you type whether the address is free.

A prefix belongs to whoever uses it first. Once acme is yours, every name inside it is yours too — and nobody else can put anything there.

Live or draft

The switch at the top of Embed & share decides whether the public pages serve this calculator at all. Turn it off and the link and the embed both stop opening — your work is untouched, only the public side is closed.

Requiring a code

Under Share via link you can ask for a six-character code before the form will open: numbers and capital letters, like 1A2D4F. Visitors are shown a card asking for it, and are not asked again on that browser.

The code guards the link, not the embed. A calculator embedded on your own website has no code in front of it — your visitors were never given one. Treat it as the lock on a meeting-room door rather than as a password: it stops a forwarded link being useful, and it is not a substitute for keeping something off the internet.
Changes go live when you publish. Editing a published calculator does not change what visitors see until you press Publish again.

Restricting which sites can use it #

In the Publish panel, under Where it may be used, switch on Only allow my websites and add your domains with Add a website.

Anywhere else, the calculator refuses to load. Leave it off and the calculator works wherever it is embedded.

Add every domain you actually use, including the www. version if your site serves it. A missing one shows as a blank space where the calculator should be.

AI builder

Describing a calculator in words and having it built, then editing what comes back.

Building with AI #

Describe the calculator you want and the AI builds it — fields, layout, formulas and results — then you adjust anything by hand.

  1. Open a calculator in the builder.
  2. Press Build with AI.
  3. Describe what you need in plain English.
  4. Review what it built, and edit anything as normal.

Be specific about what goes in and what should come out. “A quote calculator” leaves everything to guesswork; “ask for room width, room length and finish quality, then show the material cost, labour cost and total” does not.

The AI builds and edits calculator elements. Whole-calculator settings — theme, Google Sheets, PDFs, leads — are set in the Options menu, which is what this documentation is for.

Prompt examples #

Mortgage calculator

A finance calculator with a rate, a term and several results derived from the same inputs.

I need a mortgage calculator to estimate monthly home loan payments. My plan:

(1) Inputs:
- Home price (currency, set default value to 250000)
- Deposit (currency, set default value to 25000)
- Annual interest rate (percentage, set default value to 5.5)
- Loan term in years (set default value to 25)

(2) Results:
- Result 1 Label: Amount borrowed
- Result 2 Label: Monthly payment
- Result 3 Label: Total interest paid

(3) Pricing: no pricing table needed.
Tiling / flooring quote

A trade quote: measurements, a wastage allowance, and prices that have to stay editable after the calculator is built.

I need a tiling calculator to quote a flooring job. My plan:

(1) Inputs:
- Room width in metres (set default value to 4)
- Room length in metres (set default value to 5)
- Tile grade (dropdown: Standard, Premium, Luxury)
- Wastage percentage (set default value to 10 and keep input hidden)

(2) Results:
- Result 1 Label: Area (m²)
- Result 2 Label: Tiles needed including wastage
- Result 3 Label: Total cost

(3) Pricing: create a lookup table of price per m² for each tile grade, so I can edit the prices later without changing the formula.
Taxi fare calculator

A per-unit charge on top of a fixed fee, with a surcharge that only applies some of the time.

I need a taxi fare calculator for my private hire company. My plan:

(1) Inputs:
- Distance in miles (set default value to 5)
- Waiting time in minutes (set default value to 0)
- Time of day (radio: Daytime, Evening, Night)

(2) Results:
- Result 1 Label: Base fare
- Result 2 Label: Distance charge
- Result 3 Label: Waiting charge
- Result 4 Label: Total fare

(3) Pricing: create a lookup table for the rate per mile at each time of day, so I can change rates later.
Lead capture quote form

A calculator that collects an enquiry as well as showing a figure. Naming the button action is what makes it actually submit.

I need a window cleaning quote that also collects the customer's details. My plan:

(1) Inputs:
- Number of windows (set default value to 10)
- Property type (dropdown: House, Flat, Commercial)
- Name, email and phone for the enquiry

(2) Results:
- Result 1 Label: Estimated price

(3) Pricing: a lookup table of price per window by property type.

Add a button with action "submit" so the enquiry is sent to me, and email the leads to quotes@example.com.
Savings goal

Works backwards from a target rather than forwards from inputs.

I need a savings calculator that works out what someone must put aside each month. My plan:

(1) Inputs:
- Savings goal (currency, set default value to 10000)
- Amount already saved (currency, set default value to 0)
- Years to reach the goal (set default value to 3)

(2) Results:
- Result 1 Label: Still to save
- Result 2 Label: Monthly amount needed

(3) Pricing: no pricing table needed.
BMI with a written verdict

Shows that a result can be a sentence, not only a number - a result paragraph driven by an IF().

I need a BMI calculator in metric. My plan:

(1) Inputs:
- Weight in kilograms (set default value to 70)
- Height in centimetres (set default value to 175)

(2) Results:
- Result 1 Label: BMI
- Result 2 Label: Category, as a result paragraph that says whether the figure is underweight, healthy, overweight or obese

(3) Pricing: no pricing table needed.
Let the AI plan it

When the shape is not clear yet. Say what the calculator is for and let it choose the fields.

I need a calculator for my landscaping business to price a garden makeover. My plan:

(1) Inputs: Auto plan
(2) Results: Auto plan
(3) Pricing: auto plan a lookup table that I can edit later.

Account and billing

Your profile, plan limits, invoices and everything to do with billing.

Plans and subscriptions #

Every account starts with a 30-day free trial with everything unlocked. After that, choose a plan under Billing & Plan.

  • Silver — 3 calculators
  • Gold — 50 calculators
  • Platinum — unlimited calculators

Every plan includes the whole product. The only difference is how many calculators can be active at once.

Changing plan

  • Upgrading takes effect immediately, and you are charged only the difference for the days remaining.
  • Downgrading takes effect at your next renewal, so you keep what you have already paid for. You can cancel a scheduled downgrade any time before it happens.

Cancelling

Press Cancel subscription. You keep your plan until the end of the period you have paid for, and it will not renew. You can reactivate any time before it runs out.

Nothing is ever deleted when a plan ends or a trial expires. Calculators over your plan’s limit are switched off, and switch straight back on when you upgrade again.

Webhook and CRM integration #

Every calculator can post its submissions to a webhook the moment a visitor submits. Any CRM that accepts an incoming webhook, or an automation tool such as Zapier or Make, can take it from there.

  1. Open the calculator in the builder.
  2. Go to Options.
  3. Choose Integrations.
  4. Paste the webhook address your CRM gave you.

Set a signing secret in the same panel. Every request is then signed, so your server can prove it came from us and not from somebody who guessed your address.

What arrives

A POST with a JSON body:

{
  "event": "submission",
  "sent_at": "2026-08-17T10:30:00+00:00",
  "calculator": {
    "id": 42,
    "name": "Solar savings",
    "key": "d0a18f0ad6557537"
  },
  "inputs": {
    "your_name": "Sam Lee",
    "email": "sam@example.com",
    "roof_area": "48"
  },
  "results": {
    "annual_saving": "1240.00",
    "payback_years": "7.4"
  },
  "lead": {
    "id": 918,
    "email": "sam@example.com"
  },
  "meta": {
    "source": "embed",
    "page": "https://example.com/solar",
    "test": false
  }
}

The names inside inputs and results are your field keys, so name them well — they become the column names in whatever receives them.

meta.test is true for a test send from the builder. Check it and skip creating a real CRM record, or your pipeline fills up with tests.

Headers

X-EmbedSheets-EventWhich event this is, for example submission.
X-EmbedSheets-TimestampWhen it was sent, as a Unix timestamp.
X-EmbedSheets-Signaturesha256= followed by an HMAC of the timestamp, a full stop, and the raw body. Only sent when a signing secret is set.

Checking the signature

Hash timestamp + "." + raw body with your secret and compare. Use the raw body exactly as received — decoding the JSON and re-encoding it changes the spacing, and the signature stops matching.

<?php
$secret = 'your-signing-secret';

$raw       = file_get_contents('php://input');
$timestamp = $_SERVER['HTTP_X_EMBEDSHEETS_TIMESTAMP'] ?? '';
$signature = $_SERVER['HTTP_X_EMBEDSHEETS_SIGNATURE'] ?? '';

$expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $raw, $secret);

// hash_equals, not ==, so the comparison time gives nothing away.
if (!hash_equals($expected, $signature)) {
    http_response_code(401);
    exit('bad signature');
}

// Reject anything older than five minutes, so a captured request
// cannot be replayed at us later.
if (abs(time() - (int) $timestamp) > 300) {
    http_response_code(408);
    exit('too old');
}

$payload = json_decode($raw, true);

if (!empty($payload['meta']['test'])) {
    http_response_code(200);
    exit('test received');
}

$inputs  = $payload['inputs']  ?? [];
$results = $payload['results'] ?? [];

// Hand it to your CRM.
$contact = [
    'name'    => $inputs['your_name'] ?? '',
    'email'   => $inputs['email'] ?? '',
    'company' => $payload['calculator']['name'],
    'notes'   => 'Estimated saving: ' . ($results['annual_saving'] ?? 'n/a'),
];

$ch = curl_init('https://your-crm.example.com/api/contacts');
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => [
        'Content-Type: application/json',
        'Authorization: Bearer YOUR_CRM_TOKEN',
    ],
    CURLOPT_POSTFIELDS => json_encode($contact),
]);
curl_exec($ch);
curl_close($ch);

http_response_code(200);
echo 'ok';
import hmac, hashlib, time, requests
from flask import Flask, request

app = Flask(__name__)
SECRET = b"your-signing-secret"

@app.post("/embedsheets-webhook")
def receive():
    raw       = request.get_data()
    timestamp = request.headers.get("X-EmbedSheets-Timestamp", "")
    signature = request.headers.get("X-EmbedSheets-Signature", "")

    expected = "sha256=" + hmac.new(
        SECRET, f"{timestamp}.".encode() + raw, hashlib.sha256
    ).hexdigest()

    # compare_digest, so the comparison time gives nothing away
    if not hmac.compare_digest(expected, signature):
        return "bad signature", 401

    if abs(time.time() - int(timestamp or 0)) > 300:
        return "too old", 408

    payload = request.get_json()

    if payload.get("meta", {}).get("test"):
        return "test received", 200

    inputs  = payload.get("inputs", {})
    results = payload.get("results", {})

    requests.post(
        "https://your-crm.example.com/api/contacts",
        json={
            "name":  inputs.get("your_name", ""),
            "email": inputs.get("email", ""),
            "notes": f"Estimated saving: {results.get('annual_saving', 'n/a')}",
        },
        headers={"Authorization": "Bearer YOUR_CRM_TOKEN"},
        timeout=10,
    )

    return "ok", 200
const express = require("express");
const crypto  = require("crypto");

const app = express();
const SECRET = "your-signing-secret";

// The RAW body is what was signed, so keep it.
app.post("/embedsheets-webhook",
  express.raw({ type: "application/json" }),
  async (req, res) => {
    const raw       = req.body.toString("utf8");
    const timestamp = req.get("X-EmbedSheets-Timestamp") || "";
    const signature = req.get("X-EmbedSheets-Signature") || "";

    const expected = "sha256=" + crypto
      .createHmac("sha256", SECRET)
      .update(timestamp + "." + raw)
      .digest("hex");

    const ok = signature.length === expected.length && crypto.timingSafeEqual(
      Buffer.from(signature), Buffer.from(expected)
    );

    if (!ok) return res.status(401).send("bad signature");

    if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) {
      return res.status(408).send("too old");
    }

    const payload = JSON.parse(raw);

    if (payload.meta?.test) return res.status(200).send("test received");

    await fetch("https://your-crm.example.com/api/contacts", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "Authorization": "Bearer YOUR_CRM_TOKEN",
      },
      body: JSON.stringify({
        name:  payload.inputs.your_name ?? "",
        email: payload.inputs.email ?? "",
        notes: `Estimated saving: ${payload.results.annual_saving ?? "n/a"}`,
      }),
    });

    res.status(200).send("ok");
  });

app.listen(3000);
Answer quickly, work afterwards. Return 200 as soon as you have the payload and do the slow part - the CRM call, an email - in a queue or a background job. A receiver that waits on a slow third party will time out, and the visitor is the one left waiting.

If you would rather not wire it up yourself, our developers can build the connection for you. A small fee applies, starting from $49.

Still stuck? Get in touch — or open the builder and try it.