Adding custom buttons and actions to EspoCRM views: what it does and when to use it
A team that repeats the same step across many records, such as sending a quote, closing a case or marking a deal for approval, loses time and consistency when each person rebuilds that step by hand. Custom buttons and dropdown actions put that step one click away on the detail view, the list view or the edit view, so the action runs the same way every time. In EspoCRM, these controls are not added by editing the core code. They are declared in clientDefs metadata for the entity, pointed at a handler that performs the logic, and optionally guarded by ACL checks and visibility functions so that only the right users see the button on the right records.
Before you build one, decide where the action belongs and who should see it. A button on the detail view suits an action on one record, such as generating a document. A list view action suits bulk work, and an edit view control suits a step tied to data entry. Dropdown actions suit a group of related steps that would crowd the header. Each placement carries different permission and visibility questions, so write them down first: which roles may run the action, which record states should hide it, and whether the action changes data that a workflow or hook also touches. This is capability guidance. The page does not describe DeployOpen pricing, and the effort depends on the number and complexity of the actions you need.
Choosing the button type, placement and logic
This table compares the main choices for a custom EspoCRM button: where it appears, whether it calls the server or runs in the browser, and how its visibility is decided. Read each row as a decision point, and use the maintenance column to judge how much upkeep each option adds after an EspoCRM upgrade.
| Decision | Option | When it fits | Maintenance cost |
|---|---|---|---|
| Placement | Detail view button | The action depends on one record's state, such as approving a quote or moving a deal stage | Low: one view to test, and the record context is already loaded |
| Placement | List view or dropdown action | Users act on several records or pick an action from a menu across many rows | Moderate: list behavior, selection handling and mass-action permissions need separate testing |
| Execution | Browser-only action | The action only opens a view, copies data or navigates, with no change saved to the database | Low: little server code, but client scripts must be checked after each upgrade |
| Execution | Server-side action | The button must change, validate or create records, or enforce a rule that the user's browser cannot be trusted to apply | Higher: custom PHP or API logic, plus tests for permissions and failure paths |
| Visibility | Static visibility check | The button should show or hide based on the user's role, the entity type or a simple field value | Low: few conditions to review, though role changes still require retesting |
| Visibility | Function that inspects record state | Visibility depends on several fields, related records or the current stage of a workflow | Higher: logic must be kept in sync with field changes and formula or workflow updates |
| Access control | Button hidden only | Cosmetic convenience where the underlying action is already protected by server-side ACL checks | Low at first, but risky if the server does not enforce the same rule |
| Access control | Button hidden and server re-checks permissions | Any action that writes data, sends email or affects money, customer status or personal data | Moderate: two places to change when permissions change, which is the cost of reliable control |
How to build a custom button in EspoCRM step by step
This sequence takes a business action from a written requirement to a button that appears only for the right users and records, and that changes data only through checked server logic. Each step ends with a check so the team can confirm the result before moving on.
Confirm the entity and business action
Write one short specification that names the entity, the view where the button belongs (detail, list or edit), the user roles that need it, and the exact record change it should make. Decide whether the action only navigates or opens a dialog, or whether it writes data. Agree this with the process owner and a platform administrator. The step is done when the specification states the expected field values or status change for a given record, so the team can test the result against it later.
Define the button in clientDefs
Add the button to the menu section of the entity's
clientDefsmetadata in a custom location, not in the core files, so the change survives upgrades. Give the item a name, a label translation key and an action name that matches the handler you will write. Place it under the detail, list or edit menu as the specification requires. Rebuild the metadata cache, then sign in as an administrator. The check is that the button renders in the intended view, with the correct label, and that the other views are unchanged.Write the client-side handler
Create a custom view that extends the base view for that entity and view type, then add a method whose name matches the action in the menu definition. Keep this code limited to user interaction: confirm the action, collect any input, and call the server. Do not write field changes directly in the browser. The check is that clicking the button triggers the method, with a confirmation or a visible console message, and that no record changes yet. Use the browser developer tools to confirm the handler runs.
Add the server endpoint and access check
If the action changes data, put the logic in a server-side controller action or service method and make it verify the user's permission on that specific record. The server, not the button's visibility, is the security boundary, because any authenticated API call can reach an endpoint whether or not a button shows it. Confirm the access rules with the security owner before writing the check. The step is done when a direct API call made as a user without permission is rejected and the record stays unchanged.
Set visibility and availability rules
Limit when the button appears by adding an access requirement to the menu item and, where needed, a visibility function in the custom view that reads the record's current status or fields. Hide the button for records where the action is not valid, such as a closed opportunity, and for users outside the target role. Keep the rule in one place so reviewers can find it. The check is that the button shows or hides correctly for at least one record in each state and for each role listed in the specification.
Test with a user who lacks permission
Run the test cases from the specification with three accounts: one that should see and run the action, one that should see nothing, and one that sees the button but should be refused by the server. Sign in as each account in a staging instance, not production, and record the result of each run. Confirm the record state after every action, including the activity stream or audit entries. The step is done when every case produces the expected outcome and the refused account leaves no changes behind.
Package the change and deploy it
Move the metadata, view and server files into an extension or a version-controlled custom folder, and record the EspoCRM version they were built against. Deploy to staging first, repeat the tests from the previous step, then apply the same package to production during an agreed change window. Confirm with your platform team who will review the code when EspoCRM is upgraded, because custom views can depend on base view methods that change between versions. The step is done when production passes a short smoke test and the package is in version control.
How the client and server share the work
The browser layer decides what a user sees and what happens when they click. In EspoCRM, a button is declared in the entity's client definitions and wired to a JavaScript handler in a view or controller. The handler can open a dialog, call an API endpoint, refresh the record or navigate to another view. Visibility functions can hide or disable the button based on the record's current state, so a user does not see an action that would fail for the state of the record they are viewing. This layer exists for usability, and it is the part a determined user can change or bypass.
- Button declaration: Buttons and dropdown actions are listed in the client definitions for the entity, with a name, label and handler reference.
- Handler code: The handler is written as a view method or module, which keeps logic close to the screen it changes.
- Visibility checks: A visibility function reads the loaded model, such as status or assigned user, and decides whether to render the control.
- Dropdown grouping: Less common actions can move into a dropdown so the primary action stays prominent.
- Ask your team: Confirm the exact client definition and view extension points against the EspoCRM version you run, because they change between major releases.
Before you accept a design, check that every button you hide also has a server-side rule that rejects the same action when called directly.
The server layer is where access is decided. EspoCRM checks the user's role permissions for the entity and action every time a request reaches the API, whether it came from your button, a script or a direct call. Role permissions cover read, edit, delete and field-level access, and team or ownership scope limits which records a user can reach at all. If a custom action changes data, it must rely on these checks or add its own checks in server code. A visibility function in the browser never replaces them.
- Role permissions: Define which users may perform the underlying action, so the API rejects calls from users without that right.
- Ownership and team scope: Make sure the action only touches records the user is allowed to see.
- Custom server logic: If the action needs rules beyond roles, place them in server code that runs before the change is saved.
- API users: Remember that integrations calling the API follow the same roles, so a button-only restriction does not apply to them.
- Audit trail: Confirm that the data changes made by the action are recorded in the entity's stream or log for your review.
Test each action with a user who should be blocked, calling the endpoint directly, not only by clicking the button.
Some rules must run whenever a record is saved, regardless of which button or integration triggered the change. A custom button that sets a status or calculates a value is a weaker place for that logic, because any other path into the record skips it. EspoCRM offers Formula scripts for before-save calculation and validation, and PHP hooks for server-side business logic on save, remove and relate events. Workflows and BPM are also available with the Advanced Pack for multi-step automation. Choose the lightest mechanism that covers every path that can change the data.
- Formula: Suits calculated fields and validation in before-save scripts configured in the Entity Manager, and it also runs for API before-save scripts.
- Hooks: Suit PHP logic that must run on save, removal or relation changes and need ordering control.
- Workflows: Suit multi-step automation that the Advanced Pack provides, where a button only needs to start the process.
- Path coverage: List every way a record can change, including imports and API calls, before choosing where the rule lives.
- Ask your team: Confirm which mechanism matches your version's behavior for save ordering and transactions.
Review the button design with your engineers and ask which rules must hold on every write path.
Once a button, handler and server check work in a test instance, they need to reach production in a repeatable way. Packaging the customization as an extension keeps the front-end files, metadata and any PHP code together, so an upgrade can re-apply them and a reviewer can read one change set. Direct edits inside the core installation are harder to track and easier to lose during an upgrade. Packaging also clarifies version compatibility, since the extension declares which EspoCRM versions it supports.
- Package contents: Client definitions, handler scripts, language labels and any server files belong in one extension package.
- Version ranges: Declare the EspoCRM versions the package supports, and retest when you upgrade.
- Source control: Keep the package in a repository so changes are reviewed and can be rolled back.
- Staging first: Install the package in a non-production instance before it reaches users.
- Upgrade plan: Confirm with your team how the package will be checked during each major release, since behavior of client-side views can change.
Before release, check that the package installs cleanly on a copy of production data and that the button behaves the same for each role.
Security risks of custom actions in EspoCRM
A custom button is a new entry point into your data, and it inherits the same risks as any other route into a record. The cards below cover the failure modes we see most often when buttons and actions are added to EspoCRM views, and the checks that catch them before users do.
Hiding a button is not access control
Removing a button from a layout or hiding it with a visibility function only changes what the user sees. The same action can still be reached through the detail view URL, a list action, a direct API call or a workflow. Treat the visibility function as a usability measure and confirm that the server-side action checks the user's permissions again. Test each action by calling it directly as a user who should not have access.
Actions that bypass ACL checks
A handler that loads or updates records through internal service calls may skip the role-based checks that the UI applies. Code that reads entities with elevated access, or saves changes without the normal permission path, can let a user change records outside their team or scope. Review every handler for the entity and action it touches, and confirm that the user's own role governs the outcome.
Admin-only buttons shown to portal users
Portal users are external customers or partners with their own roles. An action built for administrators or internal staff can appear in a portal layout if the layout is shared or copied without review. Check which layouts and portal roles each button reaches, and confirm that the action refuses to run for portal users who lack the required permission. Keep administrative actions out of portal layouts altogether.
API users triggering the same action
Every button handler that performs an action through the REST API can also be triggered by an API user, whether or not the button is visible in the interface. API users authenticate with API keys, HMAC or basic authentication under role-based permissions. Confirm that the API user's role has no more access than the action requires, and that HMAC is preferred over basic authentication, which the docs do not recommend.
Changes lost or broken on upgrade
Buttons added by editing core files are overwritten or conflict when EspoCRM is upgraded. Customizations placed in the custom directory and packaged as an extension are easier to keep through upgrades, but they still need regression testing against the new version. Keep a register of every custom action with its files and owner, and rerun the action tests in a staging copy before each upgrade.
Admin accounts used for daily work
Testing and using custom actions from an administrator account hides permission errors, because administrators pass most checks. A button that works for an admin may fail or leak data for a standard user. The EspoCRM security docs advise against using admin accounts for daily work. Test every action with a standard user, a team-restricted user and a portal user, and restrict admin access by IP address where your network allows it.
Questions buyers ask about custom EspoCRM buttons
How much effort does a simple button take compared with a multi-step action?
A simple button that opens a record view or runs one permission-checked action is a small, contained change: one metadata entry, one handler and a visibility check. A multi-step action that validates data, changes several linked records, calls an external service and shows progress or errors takes much more work, because each step needs its own failure handling and testing. The effort depends less on the button itself than on the number of records it touches and the number of roles that see it. Ask a partner to break the quote into single-step actions and multi-step actions so you can see where the time goes.
What drives the cost of custom buttons up?
Cost rises with four factors:
- The number of views where the button appears, including detail, list, edit and side panel views
- Server-side logic, such as hooks, formula scripts or calls to other systems
- The permission matrix, meaning how many roles, teams and portal users need different access
- Testing across each role and record state that the button can reach
Each factor multiplies the others, so a button shown on five views for four roles needs far more verification than one shown on a single view for one role. Ask for the test matrix in the quote.
What does our team need to provide before a button can be scoped?
Your team needs to provide the business rule in plain language, including who should see the button, when it should be enabled and what it should change. You also need the user roles involved, sample records that cover normal and edge cases, and test accounts for each role so the access checks can be verified in your own environment. A written list of systems the action must call, if any, completes the picture. Missing test accounts are a common cause of schedule slips because the access behavior cannot be confirmed without them.
Can our internal staff build custom EspoCRM buttons themselves?
Yes, a team with PHP and JavaScript skills can build simple buttons by following the EspoCRM metadata and extension conventions, and an administrator with a development environment can test them. The harder part is keeping the code upgrade-safe, verifying access checks for every role and maintaining the change after each release. Staff who have not worked with the EspoCRM codebase usually need more time for the access logic than for the button itself. A practical split is to have internal staff own the business rule and acceptance tests while a specialist reviews the access checks.
How long does it take to scope a custom button project?
Scoping usually depends on how many actions are involved and how well the business rule is documented. A single button with a clear rule and a short list of roles can be scoped quickly, while a set of actions that touch several entities and external systems needs workshops with the process owners and a review of your permission model. DeployOpen does not publish fixed scoping durations. Ask a partner for the scoping steps, the inputs they need from you and the date by which the written scope will be delivered.
How do EspoCRM upgrades affect custom buttons?
Custom buttons depend on the metadata, client views and handler APIs of the EspoCRM version they were built for, so a major upgrade can change behavior that the button relies on. Buttons placed in extension packages with clear version ranges are easier to retest and adjust than changes made directly in core files, which are overwritten or conflict during upgrades. Plan a regression test of each button for every role before and after an upgrade, and ask the partner whether the quote includes that retest. The EspoCRM upgrade process itself is a separate piece of work to plan.
Do custom buttons create security risks, and how are they controlled?
A button is a user interface element, so hiding it does not protect data. The server must check access on every action, because a user who knows the request can still send it. Build each action so it verifies the user's ACL permissions for the target record, and test that a role without access is refused even when the button is hidden. Also confirm that logs record who ran the action. Your security team should review the access checks as part of acceptance.
How the button, handler and access check fit together
A custom button in EspoCRM passes through three separate checks before anything is saved, and each one is built in a different place. The button definition lives in the entity's clientDefs metadata, where the view reads it and draws the control. A visibility function named in that definition decides, in the browser, whether the button appears for the current record and user. A handler then runs in the browser when the user clicks, and it usually calls the server through the REST API or a custom controller. Treat the browser checks as user experience only. Anyone can call the API directly, so the decision that matters happens on the server.
On the server, EspoCRM checks the user's role permissions for the entity and action, and then checks access to the specific record, including team and ownership scope, before it runs the requested operation. If your handler triggers a save, the normal before-save logic, formula scripts and hooks also run, so a click can still be refused at that point. This means a button can be hidden for a user who lacks rights, yet the server still has to refuse the same action if the request is replayed. Confirm this behavior for your EspoCRM version by testing with a user who has no permission for the action, and do not rely on the visibility function to protect data.
The practical decision is where each rule belongs. Display logic goes in the visibility function, enforcement goes on the server, and the handler should only pass the record ID and intent. Test each layer separately: confirm the button is hidden in the browser, confirm the direct API call is refused, and confirm the record-level scope holds for users in other teams. Also confirm the exact controller and permission names against the EspoCRM documentation for your version before you write them into code, since these details can change between releases.
Limits to confirm in your EspoCRM environment
Custom buttons touch metadata, server code and access rules, and each one can break in a different way. The cards below list what to confirm against the docs or a test instance before you commit to a design, since the answers depend on your exact version and extensions.
Exact clientDefs options and handler classes
The clientDefs keys, handler base classes and view method names a button depends on are version specific, so copy them from the docs for your release line, not from older blog posts or forum snippets. The current major line is 10.x, and the 10.0.0 release followed 9.0.0 in January 2025, so older examples may not match. Confirm each option name, its default and where the handler file must live before writing any code.
Custom code behavior after upgrades
Files placed directly in core directories can be overwritten or conflict with changed internals during an upgrade. Check whether the button logic belongs in a custom or extension location that survives a version change, and list every custom handler so it can be retested after each upgrade. Confirm the upgrade procedure on a copy of your instance before scheduling production work.
Advanced Pack workflow compatibility
If your team uses Workflows or BPM from the Advanced Pack, a button that changes the same fields or statuses may fire conflicting automation. Map which actions are triggered by a button click and which by a workflow, then test both paths on the same record. Confirm the Advanced Pack version that matches your EspoCRM version and that its included status in your plan covers your deployment model.
Portal view behavior
Client-side views are shared across layouts, but portal users see only the entities, fields and actions their portal role allows. A button visible to internal users may appear in a portal view or be hidden there, depending on how the visibility function and layout are scoped. Test the button as an internal user, a portal user with a limited role and an API-only user to confirm that each sees the expected result.
Server-side checks versus button visibility
Hiding a button does not stop a user from calling the same action through the REST API or a direct request. Confirm that every action that changes data is also protected by server-side access checks, not just by the visibility function in the client. Review the API user permissions you grant, because an API user with broader roles can trigger the same operation.
Hook or workflow instead of a button
A button suits a deliberate user action, but a rule that must run on every save, delete or relation change belongs in a hook or workflow. Decide where the business rule must hold, then confirm in the docs for your version whether the logic can run from a before-save script, a workflow or a server hook, and what happens when records change through import or the API.
When a button is the wrong tool
A button is only a shortcut for a user who opens a record in the browser, so any rule that must hold on every save should not live in a clientDefs handler. Records also change through the REST API, CSV import, workflows, mass updates and server-side code, and none of those paths run the JavaScript behind a button. If a status change must always set a closing date, reassign an owner, or block an invalid amount, put that logic in a Formula before-save script in the Entity Manager, a hook in PHP, or a workflow where the Advanced Pack is in use. Before writing the button, list every path that can write the field and ask whether each one must follow the rule. If the answer is yes for any path, the button is at most a convenience on top of server-side enforcement.
Some work is better as a server action than a browser handler. Long-running operations, calls to external systems that need retries, and changes touching many related records are harder to make reliable from a view, and a browser that closes mid-action leaves partial state. Access rules are another boundary. An ACL check in the handler only hides the control, so the operation itself still needs the role permissions and, where relevant, the team scope checked on the server. Confirm this in a test with an API user and a restricted role, not only by opening the view as that user. Also confirm which EspoCRM version and documentation your team is building against before copying handler patterns from older examples.
A useful test is to ask who must be stopped if the button is removed. If the answer is nobody, a button is a reasonable choice for a single-user action such as opening a related document or printing a record. If the answer includes integrations, imports or other teams, move the rule into Formula, a hook or a workflow, and keep the button for the human step that remains. Document the decision next to the metadata change so the next administrator knows where the rule lives before adding a second copy in the browser.
How DeployOpen scopes and delivers EspoCRM button work
DeployOpen approaches custom buttons and actions in EspoCRM as a scoped piece of product work. We agree which views, roles and record states need an action, then build the clientDefs metadata, handlers and access checks in your own infrastructure, with tests and documentation handed over at the end.
How we scope
- Inventory each detail, list and edit view where a button or dropdown action is needed, with the business outcome it supports.
- Agree which roles, teams and record states must see each action, and which must never reach it.
- Document the written scope, deliverables and acceptance checks before build starts, since most engagements are fixed-scope.
How we build
- Define each action in clientDefs metadata and keep handlers in a custom extension so core files stay untouched.
- Pair every visibility function with a server-side ACL check, so hiding a button never replaces permission enforcement.
- Test each action against admin, standard user, team and portal roles, and confirm behavior on records that fail the condition.
How we hand over
- Package the work as an installable extension with a manifest, so your team can version, review and redeploy it.
- Document each button's purpose, metadata keys, handler logic, permission rules and the upgrade points to recheck.
- Walk your administrators and developers through the test cases so they can extend the actions without breaking access rules.
How we support
- Retest custom actions when you upgrade EspoCRM, since view and handler APIs can change between major versions.
- Offer ongoing support and upgrade work as a retainer, scoped in writing alongside the original engagement.
- Confirm with your own environment and the EspoCRM documentation which view APIs your target version exposes before committing to a design.

