Skip to content

Accept input with parameters

The primary mechanism for passing data from the user or document into your Pack is via parameters. You define the parameters in your code and the user fills them with values when they use your Pack. The same parameter mechanism is used by formulas, actions, and sync tables.

View Sample Code

Using parameters

In the formula editor parameters are entered as comma-separated values, while in the action dialog or sync table side panel they presented as input boxes.

Parameters in the formula editor

Parameters in the action builder

Parameters in the sync table settings

Defining parameters

The parameters property of a formula contains the array of parameter definitions, each one containing information about the parameter. The helper function makeParameter() is used to create these definitions, and a type, name, and description are required.

coda.makeParameter({
  type: coda.ParameterType.String,
  name: "type",
  description: "The type of cookie.",
})

See ParamDef for the full set of properties you can define for a parameter.

Accessing parameter values

At runtime, the values set by the user are passed to the formula's execute function as the first argument, bundled up as an array.

pack.addFormula({
  // ...
  parameters: [
    coda.makeParameter({
      type: coda.ParameterType.String,
      name: "type",
      description: "The type of cookie.",
    }),
    coda.makeParameter({
      type: coda.ParameterType.Number,
      name: "num",
      description: "How many cookies.",
    }),
  ],
  // ...
  execute: async function ([type, num], context) {
    // ...
  },
});

The order that you define the parameters determines the order they are passed into the execute function. The names of the parameters don't need to match the variable names you use for them in the execute function, but it's usually more readable to keep them the same.

Array destructuring

In the code above, and across our other samples, we typically use array destructuring to pull values out of the parameter array and assign them to variables. You could alternatively do that within the body of the execute function:

execute: async function (parameters, context) {
  let word = parameters[0];
  let count = parameters[1];
},

Parameter types

When defining a parameter you must specify what type of data the parameter will accept. The enum ParameterType lists all of the allowed parameter types.

Plain text

Use the String parameter to pass a plain text value to your formula. Coda will automatically apply the ToText() formula to the input and pass it to the execute function as a JavaScript String.

String parameters are compatible with almost every column type in Coda, as most have a text representation. At times a string parameter may be better than a more semantically accurate type, as it allows you to access the value as shown to the user.

Example: Hello world formula
import * as coda from "@codahq/packs-sdk";
export const pack = coda.newPack();

// Say Hello to the given name.
pack.addFormula({
  name: "Hello",
  description: "A Hello World example.",
  parameters: [
    coda.makeParameter({
      type: coda.ParameterType.String,
      name: "name",
      description: "The person's name you would like to say hello to.",
    }),
  ],
  resultType: coda.ValueType.String,
  execute: async function ([name]) {
    return "Hello " + name + "!";
  },
});

Rich text

Use the Html or Markdown parameter type to pass text values with formatting included. Coda will convert the formatting to an equivalent block of HTML or Markdown markup, and pass it to the execute function as a JavaScript String.

These parameter types don't provide perfect fidelity, and the converted HTML or Markdown may be quite different than how the value displays in Coda. Often it is closer to what you'd get if you pasted that value into another rich text editor.

HTML markup may change

The generated HTML for a given value is not a stable API surface that you should rely on. We may change it at any time without warning, so we don't recommend that you parse it to extract information. Use it for display purposes only.

Numbers

Use the Number parameter type to pass a number to your formula. Coda will automatically apply the ToNumber() formula to the input and pass it to the execute function as a JavaScript Number.

The number equivalent for some column types may not be obvious. Specifically:

  • Percent values will be converted into the equivalent fraction. For example, "75%" will be passed as 0.75.
  • Date and Date and time values will be converted into the number of days since 1899-12-301. For example, "1955-11-12" will be passed as 20405.
  • Time and Duration values will be converted into a number of days. For example, "12 hrs" will be passed as 0.5.
Example: Pizza eaten formula
import * as coda from "@codahq/packs-sdk";
export const pack = coda.newPack();

// Formula that converts slices of a pizza into a percentage eaten.
pack.addFormula({
  name: "PizzaEaten",
  description: "Calculates what percentage of a pizza was eaten.",
  parameters: [
    coda.makeParameter({
      type: coda.ParameterType.Number,
      name: "slices",
      description: "How many slices were eaten.",
    }),
  ],
  resultType: coda.ValueType.Number,
  codaType: coda.ValueHintType.Percent,
  execute: async function ([slices], context) {
    return slices / 8;
  },
});

Booleans

Use the Boolean parameter type to pass a boolean (true/false) to your formula. Coda will pass the value to the execute function as a JavaScript Boolean.

Dates

Use the Date parameter type to pass a date value to your formula. Coda will automatically apply the ToDateTime() formula to the input and pass it to the execute function as a JavaScript Date.

JavaScript Date objects can only represent a specific moment in time. This means that they can't easily represent less specific concepts like a day (regardless of time), a time (regardless of day), or duration. Coda handles those column types using the following logic:

  • Date values will be converted into a datetime representing midnight on that day in the document's timezone.
  • Time and Duration values will be converted a datetime that is that much time past midnight on 1899-12-301, in the document's timezone. For example, the duration "12 hours" in a document set to "America/New York" will be passed as Sat Dec 30 1899 12:00:00 GMT-0500 (Eastern Standard Time).

Timezone shifting

Because of how timezones work in Coda and JavaScript, the date passed into the parameter may appear different in your Pack code. See the Timezones guide for more information.

Example: Good New Years Eve glasses formula
import * as coda from "@codahq/packs-sdk";
export const pack = coda.newPack();

pack.addFormula({
  name: "GoodNYEGlasses",
  description: "Determines if a date is good for New Years Eve glasses " +
    "(the year contains two zeros).",
  parameters: [
    coda.makeParameter({
      type: coda.ParameterType.Date,
      name: "date",
      description: "The input date.",
    }),
  ],
  resultType: coda.ValueType.Boolean,
  execute: async function ([date], context) {
    // Format the JavaScript Date into a four-digit year.
    let formatted = date.toLocaleDateString("en", {
      timeZone: context.timezone, // Use the timezone of the doc (important!).
      year: "numeric",
    });
    // Extract all of the zeros from the year.
    let zeros = formatted.match(/0/g);
    return zeros?.length >= 2;
  },
});

Images and files

Use the Image parameter type to pass an image to your formula, and the File type for files. The value passed to the execute function will be the URL of that image or file.

Images and files that the user uploaded to the doc will be hosted on the codahosted.io domain and don't require authentication to access. These URLs are temporary, and you should not rely on them being accessible after the Pack execution has completed.

If you need access to the binary content of the image or file you'll need to use the fetcher to retrieve it. The fetcher is automatically allowed access to the codahosted.io domain, so no need to declare it as a network domain. It's not possible to access the binary content of images coming from an Image URL column, since they can come from any domain.

Example: Image file size formula
import * as coda from "@codahq/packs-sdk";
export const pack = coda.newPack();

// Regular expression that matches Coda-hosted images.
const HostedImageUrlRegex = new RegExp("^https://(?:[^/]*\.)?codahosted.io/.*");

// Formula that calculates the file size of an image.
pack.addFormula({
  name: "FileSize",
  description: "Gets the file size of an image, in bytes.",
  parameters: [
    coda.makeParameter({
      type: coda.ParameterType.Image,
      name: "image",
      description:
        "The image to operate on. Not compatible with Image URL columns.",
    }),
  ],
  resultType: coda.ValueType.Number,
  execute: async function ([imageUrl], context) {
    // Throw an error if the image isn't Coda-hosted. Image URL columns can
    // contain images on any domain, but by default Packs can only access image
    // attachments hosted on codahosted.io.
    if (!imageUrl.match(HostedImageUrlRegex)) {
      throw new coda.UserVisibleError("Not compatible with Image URL columns.");
    }
    // Fetch the image content.
    let response = await context.fetcher.fetch({
      method: "GET",
      url: imageUrl,
      isBinaryResponse: true, // Required when fetching binary content.
    });
    // The binary content of the response is returned as a Node.js Buffer.
    // See: https://nodejs.org/api/buffer.html
    let buffer = response.body as Buffer;
    // Return the length, in bytes.
    return buffer.length;
  },
});
Get the original filename

The original filename of an uploaded image or file can be obtained from the Content-Disposition header returned when fetching its codahosted.io URL.

Content-Disposition: attachment; filename="cat1.png"

This filename is not populated in all cases, for instance when an image was pasted into a table from the clipboard. You can see an example of how to parse this header, and fallback in cases where the filename isn't present, in the Box sample Pack.

Lists

Each of the parameter types described above has an array variant that allows you to pass a list of values of that type. For example, StringArray and NumberArray. All of the values in the list must be of the same type and not blank.

Example: Longest string formula
import * as coda from "@codahq/packs-sdk";
export const pack = coda.newPack();

pack.addFormula({
  name: "Longest",
  description: "Given a list of strings, returns the longest one.",
  parameters: [
    coda.makeParameter({
      type: coda.ParameterType.StringArray,
      name: "strings",
      description: "The input strings.",
    }),
  ],
  resultType: coda.ValueType.String,
  execute: async function ([strings], context) {
    if (strings.length === 0) {
      throw new coda.UserVisibleError("No options provided.");
    }
    let result;
    for (let str of strings) {
      if (!result || str.length > result.length) {
        result = str;
      }
    }
    return result;
  },
});

Table column

Passing a table column into an array parameter can be error prone, because if the column contains blank cells the formula will fail to run. To accept a list that may include blank values use the sparse variant of the array parameter (SparseStringArray, SparseNumberArray, etc). Blank cells will be represented as null, and you'll need to make sure your code can handle those values.

Example: Total cost formula
import * as coda from "@codahq/packs-sdk";
export const pack = coda.newPack();

pack.addFormula({
  name: "TotalCost",
  description: "Calculates the total cost for an order.",
  parameters: [
    coda.makeParameter({
      type: coda.ParameterType.SparseNumberArray,
      name: "prices",
      description: "The prices for each item.",
    }),
    coda.makeParameter({
      type: coda.ParameterType.SparseNumberArray,
      name: "quantities",
      description: "The quantities of each item. Default: 1.",
      optional: true,
    }),
    coda.makeParameter({
      type: coda.ParameterType.SparseNumberArray,
      name: "taxRates",
      description: "The tax rates for each item. Default: 0.",
      optional: true,
    }),
  ],
  resultType: coda.ValueType.Number,
  codaType: coda.ValueHintType.Currency,
  execute: async function ([prices, quantities=[], taxRates=[]], context) {
    if ((quantities.length > 0 && quantities.length !== prices.length) ||
        (taxRates.length > 0 && taxRates.length !== prices.length)) {
      throw new coda.UserVisibleError("All lists must be the same length.");
    }
    let result = 0;
    for (let i = 0; i < prices.length; i++) {
      let price = prices[i];
      let quantity = quantities[i];
      let taxRate = taxRates[i];
      if (price == null) {
        // If the price is blank, continue on to the next row.
        continue;
      }
      if (quantity != null) {
        price *= quantity;
      }
      if (taxRate != null) {
        price += price * taxRate;
      }
      result += price;
    }
    return result;
  },
});

Pages

Coda documents can contain many pages, and it's possible to pass the contents of a page to a Coda formula using the Html parameter type. Users will be presented with the pages they can select from in the autocomplete options, and once selected the formula will receive an HTML version of that page's content. Some features of the page may not be included in the HTML markup, and it should not be considered a complete or stable API surface.

Formulas not recalculated when page content changes

Unlike with other data sources, when passing a page as a parameter the formula will not be automatically recalculated when the content of the page changes. For this reason we recommend only passing pages for action formulas, which are calculated on each button press or automation run.

Objects

Pack formulas can return structured data as Objects, but it's not possible to pass them as parameters. Users can't construct objects in the Coda formula language, so in general they don't make for a great input type.

If your Pack returns an object in one formula that you'd like to use an input to another formula, instead of passing the entire object you can just pass its unique ID. For example, the Todoist Pack contains a Tasks sync table which returns Task objects. The MarkAsComplete() formula only takes the task's ID as input instead of the entire object.

Optional parameters

By default all parameters you define are required. To make a parameter optional simply add optional: true to your parameter definition. Optional parameters are shown to the user but not required in order for the formula to execute. Optional parameters must be defined after all of the required parameters, and like required parameters their order is reflected in the Coda formula editor and the array of values passed to the execute function.

pack.addFormula({
  name: "TotalCost",
  // ...
  parameters: [
    coda.makeParameter({
      type: coda.ParameterType.Number,
      name: "cost",
      description: "The cost of the item.",
    }),
    coda.makeParameter({
      type: coda.ParameterType.Number,
      name: "quantity",
      description: "How many items.",
      optional: true,
    }),
    coda.makeParameter({
      type: coda.ParameterType.Number,
      name: "taxRate",
      description: "The tax rate for the item.",
      optional: true,
    }),
  ],
  // ...
  execute: async function ([cost, quantity, taxRate], context) {
    // ...
  },
});

Optional parameters that have not been set by the user will default to the JavaScript value undefined in your execute function. When you initialize your parameter variables in the execute function you can assign a default value that will get used when the parameter has not been explicitly set by the user.

pack.addFormula({
  // ...
  execute: async function ([cost, quantity = 1, taxRate = 0], context) {
    // ...
  },
});

When using a formula with optional parameters, the user may choose to set those parameters by name, instead of by position. This can be useful when they want to skip over some optional parameters that appear earlier in the list.

TotalCost(10, taxRate: 0.15)

In this case the cost and taxRate parameters would be set, but the quantity parameter would be undefined, and therefore use its default value of 1.

Example: Scream text formula
import * as coda from "@codahq/packs-sdk";
export const pack = coda.newPack();

// Formats text to look like screaming. For example, "Hello" => "HELLO!!!".
pack.addFormula({
  name: "Scream",
  description: "Make text uppercase and add exclamation points.",
  parameters: [
    coda.makeParameter({
      type: coda.ParameterType.String,
      name: "text",
      description: "The text to scream.",
    }),
    coda.makeParameter({
      type: coda.ParameterType.Number,
      name: "volume",
      description: "The number of exclamation points to add.",
      optional: true,
    }),
    coda.makeParameter({
      type: coda.ParameterType.String,
      name: "character",
      description: "The character to repeat.",
      optional: true,
    }),
  ],
  resultType: coda.ValueType.String,
  examples: [
    { params: ["Hello"], result: "HELLO!!!" },
    { params: ["Hello", 5], result: "HELLO!!!!!" },
    { params: ["Hello", undefined, "?"], result: "HELLO???" },
    { params: ["Hello", 5, "?"], result: "HELLO?????" },
  ],
  execute: async function ([text, volume = 3, character = "!"], context) {
    return text.toUpperCase() + character.repeat(volume);
  },
});

Suggested values

As a convenience to users of your Pack, you can provide a suggested value for a parameter. When they use your formula the default will be pre-populated in the formula editor, action dialog, etc. The user is then free to edit or replace it this value.

To add a suggested value to a parameter set the field suggestedValue to the value you'd like to use. The suggested value must be of the same type as the parameter, for example a number parameter must have a number as its suggested default value.

coda.makeParameter({
  type: coda.ParameterType.Number,
  name: "days",
  description: "How many days of data to fetch.",
  suggestedValue: 30,
})

Currently suggested values are only used for required parameters, and setting them for optional parameters has no effect.

Example: Random dice roll action
import * as coda from "@codahq/packs-sdk";
export const pack = coda.newPack();

// Rolls virtual dice and returns the resulting numbers. Use it with a button in
// table and store the results in another column.
pack.addFormula({
  name: "RollDice",
  description: "Roll some virtual dice.",
  parameters: [
    coda.makeParameter({
      type: coda.ParameterType.Number,
      name: "quantity",
      description: "How many dice to roll.",
      suggestedValue: 1,
    }),
    coda.makeParameter({
      type: coda.ParameterType.Number,
      name: "sides",
      description: "How many sides the dice have.",
      suggestedValue: 6,
    }),
  ],
  resultType: coda.ValueType.Array,
  items: coda.makeSchema({
    type: coda.ValueType.Number,
  }),
  isAction: true,
  execute: async function ([quantity, sides], context) {
    let results = [];
    for (let i = 0; i < quantity; i++) {
      let roll = Math.ceil(Math.random() * sides);
      results.push(roll);
    }
    return results;
  },
});

Accepting multiple values

For some formulas you may want to allow the user to enter multiple values for a parameter. You could use an array parameter for this case but a more user-friendly approach may be to use variable argument (vararg) parameters. These are parameters that you allow the user to repeat as many times as needed.

Foo(List("A", "B", "C"))  # A string array parameter.
Foo("A", "B", "C")        # A string variable argument parameter.

They are defined using the varargParameters property and accept the same parameter objects. The values set by the user are passed in to the execute just like normal parameters, only there is an unknown number of them. The easiest way to access them is by using JavaScript's "rest" syntax, which captures the remaining values into an array.

pack.addFormula({
  // ...
  parameters: [
    coda.makeParameter({
      type: coda.ParameterType.String,
      name: "name",
      description: "The person's name.",
    }),
  ],
  varargParameters: [
    coda.makeParameter({
      type: coda.ParameterType.String,
      name: "nickname",
      description: "A nickname for the person.",
    }),
  ],
  // ...
  execute: async function ([name, ...nicknames], context) {
    // ...
  },
});

There are some important differences between vararg parameters and standard parameters:

  • They appear at the end of the formula, after all standard parameters.
  • Unlike standard parameters they are optional by default, and cannot by made required.
  • You can't provide a default value, since the user must always enter an explicit value.
  • You can have more than one, but if so the user is required to enter complete sets of values. For example, if you have two vararg parameters a and b, the user can't provide a value for a without also providing a value for b. These pairs of parameters can then be repeated multiple times: Foo("a1", "b1", "a2", "b2").
Partially supported in actions builder or sync table settings

While vararg parameters always work in the formula editor, they are only partially supported in the builder UIs. A single vararg parameter will be shown as if it was a single array parameter, and a pair of vararg parameters will be shown with a nice UI similar to that used by built-in actions. Three or more vararg parameters won’t show up in the builder UIs at all, and the user will need to visit the formula editor to set their values.

Example: Step diagram formula
import * as coda from "@codahq/packs-sdk";
export const pack = coda.newPack();

// Takes an unknown number of steps and labels and outputs a simple diagram.
// Example: Steps("Idea", "Experiment", "Prototype", "Refine", "Product")
// Result: Idea --Experiment--> Prototype --Refine--> Product
pack.addFormula({
  name: "Steps",
  description: "Draws a simple step diagram using text.",
  parameters: [
    coda.makeParameter({
      type: coda.ParameterType.String,
      name: "start",
      description: "The starting step.",
    }),
  ],
  varargParameters: [
    coda.makeParameter({
      type: coda.ParameterType.String,
      name: "label",
      description: "The label for the arrow.",
    }),
    coda.makeParameter({
      type: coda.ParameterType.String,
      name: "step",
      description: "The next step.",
    }),
  ],
  resultType: coda.ValueType.String,
  execute: async function ([start, ...varargs], context) {
    let result = start;
    while (varargs.length > 0) {
      let label; let step;
      // Pull the first set of varargs off the list, and leave the rest.
      [label, step, ...varargs] = varargs;
      result += ` --${label}--> ${step}`;
    }
    return result;
  },
});

Autocomplete

If you have a parameter that accepts a limited set of values it's usually best to provide those options using autocomplete. See the Autocomplete guide for more information.

Reusing parameters

It's often the case that many formulas in a Pack use the same parameter. For example, the Google Calendar Pack has many formulas have a parameter for the calendar to operate on. Rather than redefine the same parameter for each formula, it can be more efficient to define the shared parameter once outside of a formula and then reuse it multiple times.

const ProjectParam = coda.makeParameter({
  type: coda.ParameterType.String,
  name: "projectId",
  description: "The ID of the project.",
});

pack.addFormula({
  name: "Project",
  description: "Get a project.",
  parameters: [
    ProjectParam,
  ],
  // ...
});

pack.addFormula({
  name: "Task",
  description: "Get a task within a project.",
  parameters: [
    ProjectParam,
    coda.makeParameter({
      type: coda.ParameterType.String,
      name: "taskId",
      description: "The ID of the task.",
    }),
  ],
  // ...
});
Example: Math formulas
import * as coda from "@codahq/packs-sdk";
export const pack = coda.newPack();

// A "numbers" parameter shared by both formulas.
const NumbersParameter = coda.makeParameter({
  type: coda.ParameterType.NumberArray,
  name: "numbers",
  description: "The numbers to perform the calculation on.",
});

pack.addFormula({
  name: "GCD",
  description: "Returns the greatest common divisor for a list of numbers.",
  parameters: [
    // Use the shared parameter created above.
    NumbersParameter,
  ],
  resultType: coda.ValueType.Number,
  execute: async function ([numbers]) {
    // Handle the error case where the list is empty.
    if (numbers.length === 0) {
      throw new coda.UserVisibleError("The list cannot be empty.");
    }

    // Handle the error case where all the numbers are zeros.
    if (numbers.every(number => number === 0)) {
      throw new coda.UserVisibleError(
        "The list must contain a non-zero number.");
    }

    let result = numbers[0];
    for (let i = 1; i < numbers.length; i++) {
      let number = numbers[i];
      result = gcd(number, result);
    }
    return result;
  },
});

pack.addFormula({
  name: "LCM",
  description: "Returns the least common multiple for a list of numbers.",
  parameters: [
    // Use the shared parameter created above.
    NumbersParameter,
  ],
  resultType: coda.ValueType.Number,
  execute: async function ([numbers]) {
    // Handle the error case where the list is empty.
    if (numbers.length === 0) {
      throw new coda.UserVisibleError("The list cannot be empty.");
    }

    // Handle the error case where the list contains a zero.
    if (numbers.some(number => number === 0)) {
      throw new coda.UserVisibleError("The list must not contain a zero.");
    }

    let result = numbers[0];
    for (let i = 1; i < numbers.length; i++) {
      let number = numbers[i];
      result = Math.abs(number * result) / gcd(number, result);
    }
    return result;
  },
});

// Helper function that calculates the greatest common divisor of two
// numbers.
function gcd(a, b) {
  if (a === 0) {
    return b;
  }
  return gcd(b % a, a);
}

Date range parameters

Parameters of the type DateArray are often used for date ranges, with the first date representing the start of the range and the second date representing the end. When a DateArray parameter is used in an action or sync table the the input box displays a date range picker to make it easier for the user to select a range.

Date array parameters displayed as a date range picker

These parameters also support a special set of suggested values that represent date ranges relative to the current date. These are available in the PrecannedDateRange enumeration.

coda.makeParameter({
  type: coda.ParameterType.DateArray,
  name: "dateRange",
  description: "The date range over which data should be fetched.",
  suggestedValue: coda.PrecannedDateRange.Last30Days,
})

The table below shows the recommended parameter type to use with various types of Coda columns and values.

Type Supported Recommended Notes
Text ✅ Yes String Use Html or Markdown if the formatting is important.
Link ✅ Yes String
Canvas ✅ Yes Html Use String to discard formatting.
Select list ✅ Yes StringArray Works for both single and multi-value select lists.
Number ✅ Yes Number
Percent ✅ Yes Number Passed as a fraction.
Currency ✅ Yes Number Use String to get currency symbol.
Slider ✅ Yes Number
Scale ✅ Yes Number
Date ✅ Yes Date
Time ✅ Yes Date
Date and time ✅ Yes Date
Duration ✅ Yes Number
Checkbox ✅ Yes Boolean
People ❌ No Use String to get the person's name.
Email ✅ Yes String
Reaction ❌ No Use StringArray to get the names of the people that reacted.
Button ❌ No
Image ✅ Yes ImageArray Image column can contain multiple images.
Image URL ✅ Yes Image
File ✅ Yes FileArray File columns can contain multiple files.
Relation ❌ No Use StringArray to get the display name of the row(s).
Table ❌ No You can't pass an entire table, pass individual columns instead.
Page ✅ Yes Html

  1. The representation is known as "serial number" and is common to all major spreadsheet applications.