---
title: "Custom Buttons"
description: "Add buttons to OnePageCRM contact and deal menus that open your app at the right record, using URL template variables."
canonical_url: https://developer.onepagecrm.com/integrations/custom-button/
source: Markdown mirror of https://developer.onepagecrm.com/integrations/custom-button/
---

Custom Buttons let you add your own links to the **3-dots menu** on
contact and deal views. Each button opens a URL you define, with
template variables resolved from the current record — so one click
lands the user in your app, on the right customer.

Use it to:

- Deep-link into your app preloaded with the right context
  ("Open in Acme" → your customer page for that contact).
- Trigger an external workflow next to the record it relates to
  (create an invoice, start a call, open a booking form).
- Bridge to internal tools that key off an email, phone number, or
  [External ID](/integrations/external-id/).

> Custom Buttons is currently in **beta**. If you don't see it on your
> Apps page, contact OnePageCRM support to get it enabled.

## Set it up

Buttons are configured in the OnePageCRM web app — there's no public
API for managing them.

1. In OnePageCRM, open **Apps** and install **Custom Buttons**
   (`app.onepagecrm.com/app/custom_buttons`).
2. Create a button and fill in the fields below. You need to be an
   **account admin** to create, edit, or delete buttons.
3. Open any contact (or deal) and check the 3-dots menu — your button
   is there for every user on the account.

### Button fields

| Field | Required | Notes |
| --- | --- | --- |
| Name | yes | The label shown in the menu. |
| URL template | yes | Must start with `http://` or `https://`. May contain template variables. |
| Context | yes | `contact` or `deal` — which 3-dots menu the button appears in. Add two buttons to cover both. |
| Icon | no | One of `default`, `call`, `mail`, `money`, `profile`, `tool`. |
| Enabled | — | Toggle a button off without deleting it. |

You can create up to **25 buttons per context** (25 for contacts, 25
for deals).

## Template variables

Variables use the form `[entity.field]`. When a user clicks the
button, each variable is replaced with the value from the current
record and **URL-encoded** automatically.

### Contact variables

| Variable | Value |
| --- | --- |
| `[contact.id]` | OnePageCRM contact ID |
| `[contact.firstname]` | First name |
| `[contact.lastname]` | Last name |
| `[contact.fullname]` | Full name |
| `[contact.title]` | Title (Mr, Mrs, Ms) |
| `[contact.jobtitle]` | Job title |
| `[contact.email]` | First email address |
| `[contact.phone]` | First phone number |
| `[contact.address]` | First address, formatted as one line |

### Organization variables

| Variable | Value |
| --- | --- |
| `[organization.id]` | OnePageCRM company ID |
| `[organization.name]` | Company name |

### Deal variables

| Variable | Value |
| --- | --- |
| `[deal.id]` | OnePageCRM deal ID |
| `[deal.name]` | Deal name |
| `[deal.amount]` | Deal amount |
| `[deal.totalamount]` | Total amount (amount × months) |

### Custom field variables

Every custom field is available as
`[contact.cf.<name>]`, `[organization.cf.<name>]`, or
`[deal.cf.<name>]`, where `<name>` is the field's name lowercased with
spaces replaced by underscores. A field named **Acme ID** becomes:

```
[contact.cf.acme_id]
```

The configuration screen lists every available variable for your
account, including your custom fields — pick from the list rather than
typing them by hand.

### Fallbacks

Provide a default with the `fallback` option:

```
[contact.cf.acme_id, fallback=unknown]
```

The fallback fires only when a variable resolves to **no value at
all** — not an empty string. In practice it works for: custom field
variables with no value, unset name and title fields,
`organization.*` variables when the contact has no company, an unset
`deal.name`, and any variable whose linked record is missing. It does
**not** fire for `[contact.email]`, `[contact.phone]`, or
`[contact.address]` — those resolve to an empty string when absent.
IDs and deal amounts always have a value, so a fallback on them never
fires.

## Example: open your app at the right customer

Say you sync customers with an
[External ID](/integrations/external-id/) custom field named
**Acme ID**. Add a contact-context button:

| Field | Value |
| --- | --- |
| Name | `Open in Acme` |
| Context | `contact` |
| URL template | `https://app.acme.com/customers/[contact.cf.acme_id]` |

A contact whose Acme ID is `cus_8c1ab2` gets a menu item that opens:

```
https://app.acme.com/customers/cus_8c1ab2
```

No External ID yet? Key off email instead and resolve it on your side:

```
https://app.acme.com/lookup?email=[contact.email]&name=[contact.fullname]
```

## Behavior notes

- Buttons open in a **new browser tab** (`target="_blank"` with
  `rel="noopener noreferrer"`).
- Contact-context buttons resolve `contact.*` and `organization.*`
  variables. Deal-context buttons resolve `deal.*` plus the linked
  contact's `contact.*` and `organization.*` variables.
- Variables are resolved when the record view loads. Your
  endpoint receives a plain GET from the user's browser — there is no
  signature or auth handoff. Treat incoming parameters as hints and
  authenticate the user in your own app as usual.
- Buttons are account-wide: every user sees enabled buttons, but only
  admins can manage them.

## See also

- [External ID](/integrations/external-id/) — give every record a
  stable ID in your system, then link straight to it.
