Deep-dive on the Next Gen Platform. Join the Webinar!

Skip Navigation
Show nav
Dev Center
  • Get Started
  • Documentation
  • Changelog
  • Search
  • Get Started
    • Node.js
    • Ruby on Rails
    • Ruby
    • Python
    • Java
    • PHP
    • Go
    • Scala
    • Clojure
    • .NET
  • Documentation
  • Changelog
  • More
    Additional Resources
    • Home
    • Elements
    • Products
    • Pricing
    • Careers
    • Help
    • Status
    • Events
    • Podcasts
    • Compliance Center
    Heroku Blog

    Heroku Blog

    Find out what's new with Heroku on our blog.

    Visit Blog
  • Log inorSign up

Getting Started with Heroku AppLink (Pilot)

Introduction

Heroku AppLink is currently in pilot. The products offered as part of the pilot aren’t intended for production use and are considered as a Beta Service and are subject to the Beta Services terms at https://www.salesforce.com/company/legal/agreements.jsp.

Heroku AppLink (formerly Heroku Integration) exposes your Heroku apps as API services in Salesforce. This guide helps you set up the Heroku AppLink add-on via the Heroku CLI and the Heroku AppLink CLI plugin. To get an idea of what you can use Heroku AppLink for, see the Use Cases section.

In this guide, we create and import a Heroku app and use the external service in Salesforce to execute actions in flows and Apex.

This guide assumes that you have:

  • A verified Heroku account
  • Joined the Heroku AppLink pilot
  • An Eco dynos plan subscription (recommended)
  • All the dependencies based on your language of choice
  • The Salesforce developer org you get from signing up to the pilot, a sandbox org, or a scratch org
  • (Optional) Install the Salesforce CLI to work with your Salesforce org

If you use a scratch org, you must enable the HerokuIntegration feature in your scratch org definition file. For example:

{
 "orgName": "Acme",
 "edition": "Enterprise",
 "features": ["HerokuIntegration"]
}

Install the Heroku AppLink Plugin

You must have the Heroku CLI installed before adding the Heroku AppLink CLI plugin. See Heroku CLI for instructions.

To install the plugin, run the CLI command:

$ heroku plugins:install @heroku-cli/plugin-integration

You can view the plugin info with the commands:

$ heroku plugins
integration 0.0.9

$ heroku plugins:inspect @heroku-cli/plugin-integration
└─ @heroku-cli/plugin-integration
...
├─ commands
│ ├─ datacloud:connect
│ ├─ datacloud:data-action-target:create
│ ├─ datacloud:disconnect
│ ├─ integration:connections
│ ├─ integration:connections:info
│ ├─ integration:project
│ ├─ salesforce:connect
│ ├─ salesforce:disconnect
│ └─ salesforce:import

Prepare Your App

Create a local copy of the sample app by executing the following commands in your local command shell or terminal:

$ git clone https://github.com/heroku-examples/applink-getting-started-pilot
$ cd applink-getting-started-pilot

This Git repository contains a sample Node.js application that uses the Fastify web framework. The sample app has the following structure:

  • api-spec.yaml: (Required) This sample API specification file lets you import the app in Salesforce as an external service. We support OpenAPI 3.0. See External Services and OpenAPI for more info.
  • index.ts: This file provides the API implementations.
  • src/plugins/heroku-salesforce.js: This plugin is the Salesforce pre-handler that enriches the requests coming in and sets the context about the request. This file provides the Salesforce SDK with the user and org context and helps with handling asynchronous requests.
  • package.json: This file has the dependencies for the Salesforce SDK for Node.js that the plugin supports. The SDK provides an easy way to perform DML operations in Salesforce and Data Cloud.
  • Procfile: The Heroku Procfile that defines what’s executed by the app on startup. In the Procfile, heroku-integration-service-mesh starts the app.
  • bin/invoke.sh: This file invokes the local running app’s API. Specify the target org and config behavior by passing arguments.

The example project template has two API operations:

  • GetAccounts (/accounts): A synchronous GET call that returns a list of arrays of Accounts.
  • UnitOfWork (/unitofwork): An asynchronous POST call that receives a payload containing Account, Contact, and Case details. It uses the Unit of Work pattern to assign the corresponding values to the record while maintaining the relationships. It then commits the unit of work and returns the record IDs for each object.

Create Your App

Using dynos during this pilot does count towards your usage. To complete this tutorial, we recommend using our low-cost plans. Eligible students can apply for platform credits through our new Heroku for GitHub Students program.

To prepare Heroku to receive your source code, create an app:

$ heroku create
Creating app... done, ⬢ integration-app
http://integration-app.herokuapp.com/ | https://git.heroku.com/integration-app.git

Heroku generates a random name for your app.if you don’t specify a name. In the guide, our app is called integration-app.

Install the Heroku Integration Buildpack

The Heroku Buildpack for Heroku Integration Service Mesh installs the Heroku Integration Service Mesh to handle the authentication and authorization for your app. The service mesh is a proxy in front of your app that intercepts incoming Salesforce and Data Cloud requests to validate and authenticate.

To install, run the command:

$ heroku buildpacks:add https://github.com/heroku/heroku-buildpack-heroku-integration-service-mesh

Additionally, for the nodejs project, set the heroku/nodejs buildpack:

$ heroku buildpacks:add heroku/nodejs
Buildpack added. Next release on integration- will use:
  1. https://github.com/heroku/heroku-buildpack-heroku-integration-service-mesh
  2. heroku/nodejs
Run git push heroku main to create a new release using these buildpacks.

Provision the Heroku AppLink Add-on

To provision the add-on, run the command:

$ heroku addons:create heroku-integration
Creating heroku-integration on ⬢ integration-app... free
Your add-on is being provisioned.
integration-regular-78506 is being created in the background. The app will restart when complete...
Use heroku addons:info integration-regular-78506 to check creation progress
Use heroku addons:docs integration-app to view documentation

During the pilot, you can’t share connections from one app to the other. This capability is part of the roadmap.

After provisioning, the add-on creates the config vars:

  • HEROKU_INTEGRATION_URL: contains the base URL for the CLI to make requests
  • HEROKU_INTEGRATION_TOKEN: contains the access token

You can get your config vars with the heroku config command:

$ heroku config
=== integration-app Config Vars

HEROKU_INTEGRATION_API_URL: https://heroku-integration.heroku.com/addons/894792c1-c1e8-4f34-ba32-00000000000
HEROKU_INTEGRATION_TOKEN: af0a7e7984d4ef28948db6431dc036ae383fcb2f02064cbb0000000000000000

Deploy Your Heroku App

Next, deploy your app:

$ git push heroku main
Enumerating objects: 26, done.
Counting objects: 100% (26/26), done.
Delta compression using up to 36 threads
Compressing objects: 100% (22/22), done.
Writing objects: 100% (26/26), 99.72 KiB | 4.15 MiB/s, done.
Total 26 (delta 3), reused 19 (delta 0), pack-reused 0 (from 0)
remote: Updated 14 paths from 1a9844a
remote: Compressing source files... done.
remote: Building source:
remote:
remote: -----> Building on the Heroku-22 stack
remote: -----> Using buildpacks:
remote:        1. https://github.com/heroku/heroku-buildpack-heroku-integration-service-mesh
remote:        2. heroku/nodejs
remote: -----> https://github.com/heroku/heroku-buildpack-heroku-integration-service-mesh app detected
remote: -----> Installing version "v0.1.0" of Heroku Integration Service Mesh...
remote:        Downloading heroku-integration-service-mesh...
remote:        Installing heroku-integration-service-mesh...
remote:        Done!
remote: -----> Ensure that heroku-integration-service-mesh is configured in your Procfile's web process to start your app, eg heroku-integration-service-mesh <app startup command>
remote: -----> Node.js app detected
remote:
...
remote: Verifying deploy... done.
To https://git.heroku.com/integration-app.git
 * [new branch]      main -> main

Connect to a Salesforce Org

See Connections on Your App for more information about creating, viewing, and removing connections.

 

For production orgs, use “https://login.salesforce.com” for the login URL. For sandbox and scratch orgs, use “https://test.salesforce.com” for the login URL.

 

If you’re already logged into an org, this command will attempt to connect to the org you’re already logged into. If you want to connect to a different org, logout from all existing orgs before running this command.

Next, create a connection and authorize your Salesforce org. To connect to a sandbox org, run the command:

$ heroku salesforce:connect applink-org --store-as-run-as-user
Press any key to open up the browser to connect ⬢ integration-app to applink-org or q to exit:
Opening browser to https://login.salesforce.com/services/oauth2/authorize?client_id=…
Connecting ⬢ integration-app to applink-org... done
Connected org 00Dbc0000000000000 to ⬢ integration-app.

When you authorize your org for the first time, a browser window appears for you to enter your Salesforce credentials. You must accept the access levels to give Heroku. You can also pass the --store-as-run-as-user flag to store user’s credentials to use in API code. We recommend creating an Integration User specific for run-as-user connections.

To view the info on your connection, run the command:

$ heroku integration:connections:info applink-org
Id:           b6ff0867-935a-4181-a303-52ce1aefb0ae
Instance URL: https://dws000005eafxmao.sandbox.my.salesforce.com
Org ID:       00Dbc0000000000000
Org Name:     applink-org
Run As User:  jdoe+202410hkpilots@salesforce.com
State:        Connected
Type:         Salesforce Org

Import Your Heroku App

See Import Your App as an External Service for more information.

 

Importing apps requires Modify Metadata Through Metadata API Functions and Manage Profiles and Permission Sets user permissions.

You can import your app to Salesforce as an external service and then use those external service actions in Salesforce flows and Apex. To import your app, run the command:

$ heroku salesforce:import api-spec.yaml --org-name applink-org --client-name HerokuAPI --generate-auth-permission-set

Importing app ⬢ integration-app as 'HerokuAPI' to org applink-org... done

Passing the --generate-auth-permission-set flag generates a session-based permission set named <ClientName>Authorization, for example HerokuAPIAuthorization. The permission set is activated on each external service to Heroku app requests’ access token enabling the target API to have additional org access.

After importing your app, you can see the app and its input and output parameters from the Salesforce setup. From the setup page:

  1. Search for Heroku in the Quick Find and click Apps.
  2. Click the name of the app you imported.

Heroku Apps in Setup

You can also see your imported app by searching for and clicking External Services from the Quick Find.

If import fails, review the Deployment Status in the Salesforce Setup for more details.

View Logs

You can view your app’s logs with heroku logs --tail:

$ heroku logs --tail
---
2024-10-28T16:02:15.215250+00:00 app[web.1]: time=2024-10-28T16:02:15.215Z level=INFO msg="Processing request to /accounts..." app=local source=heroku-integration-service-mesh request-id=00Dbc0000000000000-f2f3ed60-2dbd-4edd-b707-9dee97d89c7c
2024-10-28T16:02:15.215254+00:00 app[web.1]: time=2024-10-28T16:02:15.215Z level=INFO msg="Validating request..." app=local source=heroku-integration-service-mesh request-id=00Dbc0000000000000-f2f3ed60-2dbd-4edd-b707-9dee97d89c7c
2024-10-28T16:02:15.215330+00:00 app[web.1]: time=2024-10-28T16:02:15.215Z level=INFO msg="Valid request!" app=local source=heroku-integration-service-mesh request-id=00Dbc0000000000000-f2f3ed60-2dbd-4edd-b707-9dee97d89c7c
2024-10-28T16:02:15.215331+00:00 app[web.1]: time=2024-10-28T16:02:15.215Z level=INFO msg="Found Salesforce request" app=local source=heroku-integration-service-mesh request-id=00Dbc0000000000000-f2f3ed60-2dbd-4edd-b707-9dee97d89c7c
2024-10-28T16:02:15.215332+00:00 app[web.1]: time=2024-10-28T16:02:15.215Z level=INFO msg="Authenticating Salesforce request for org 00Dbc0000000000000, domain https://mydomain.demo.my.salesforce.com..." app=local source=heroku-integration-service-mesh request-id=00Dbc0000000000000-f2f3ed60-2dbd-4edd-b707-9dee97d89c7c
2024-10-28T16:02:15.394673+00:00 app[web.1]: time=2024-10-28T16:02:15.394Z level=INFO msg="Authenticated request!" app=local source=heroku-integration-service-mesh request-id=00Dbc0000000000000-f2f3ed60-2dbd-4edd-b707-9dee97d89c7c
2024-10-28T16:02:15.394674+00:00 app[web.1]: time=2024-10-28T16:02:15.394Z level=INFO msg="Forwarding request..." app=local source=heroku-integration-service-mesh request-id=00Dbc0000000000000-f2f3ed60-2dbd-4edd-b707-9dee97d89c7c
2024-10-28T16:02:15.395727+00:00 app[web.1]: {"level":30,"time":1730131335395,"pid":37,"hostname":"dyno-f34267e3-3d00-453d-a3b4-a16ab26a773b","reqId":"00Dbc0000000000000-f2f3ed60-2dbd-4edd-b707-9dee97d89c7c","req":{"method":"GET","url":"/accounts","hostname":"127.0.0.1:3000","remoteAddress":"127.0.0.1","remotePort":35162},"msg":"incoming request"}
2024-10-28T16:02:15.398888+00:00 app[web.1]: {"level":30,"time":1730131335396,"pid":37,"hostname":"dyno-f34267e3-3d00-453d-a3b4-a16ab26a773b","reqId":"00Dbc0000000000000-f2f3ed60-2dbd-4edd-b707-9dee97d89c7c","msg":"GET /accounts: {}"}
2024-10-28T16:02:15.398893+00:00 app[web.1]: {"level":30,"time":1730131335396,"pid":37,"hostname":"dyno-f34267e3-3d00-453d-a3b4-a16ab26a773b","reqId":"00Dbc0000000000000-f2f3ed60-2dbd-4edd-b707-9dee97d89c7c","msg":"Querying invoking org (00Dbc0000000000000) Accounts..."}
2024-10-28T16:02:15.481929+00:00 app[web.1]: {"level":30,"time":1730131335481,"pid":37,"hostname":"dyno-f34267e3-3d00-453d-a3b4-a16ab26a773b","reqId":"00Dbc0000000000000-f2f3ed60-2dbd-4edd-b707-9dee97d89c7c","msg":"For invoking org (00Dbc0000000000000), found the following Accounts: [{\"id\":\"001Ws00003GGHVCIA5\",\"name\":\"Global Media\"},{\"id\":\"001Ws00003GGHVDIA5\",\"name\":\"Acme\"},{\"id\":\"001Ws00003GGHVEIA5\",\"name\":\"salesforce.com\"}]"}
2024-10-28T16:02:15.482306+00:00 app[web.1]: 2024/10/28 16:02:15 [00Dbc0000000000000-f2f3ed60-2dbd-4edd-b707-9dee97d89c7c] "GET http://integration-app-c4effe617e07.herokuapp.com/accounts HTTP/1.1" from 18.214.12.209 - 200 267B in 267.062237ms
2024-10-28T16:02:15.482442+00:00 app[web.1]: {"level":30,"time":1730131335482,"pid":37,"hostname":"dyno-f34267e3-3d00-453d-a3b4-a16ab26a773b","reqId":"00Dbc0000000000000-f2f3ed60-2dbd-4edd-b707-9dee97d89c7c","res":{"statusCode":200},"responseTime":86.56547299958766,"msg":"request completed"}
2024-10-28T16:02:15.482623+00:00 heroku[router]: at=info method=GET path="/accounts" host=integration-app-c4effe617e07.herokuapp.com request_id=00Dbc0000000000000-f2f3ed60-2dbd-4edd-b707-9dee97d89c7c fwd="18.214.12.209" dyno=web.1 connect=0ms service=267ms status=200 bytes=267 protocol=http tls_version=tls1.3

Invoke Your Imported App Actions with Apex

See Invoke Your Imported Apps for more information.

Before you can invoke your imported app, you must assign users the following permission sets:

  • HerokuAPI
  • <imported-app-name>
  • <imported-app-name>Authorization if you provided the --generate-auth-permission-set flag during app import

You can invoke your external service in Salesforce with Apex, Salesforce Flow, Data Cloud, and Agentforce. This sample code shows how you can invoke the imported Heroku API as part of an Apex class:

Synchronous invocation:

public class InvokeHerokuAPI {
    public static void getAccounts() {
        try {
            ExternalService.HerokuAPI herokuAPI = new ExternalService.HerokuAPI();
            ExternalService.HerokuAPI.GetAccounts_Response response = herokuAPI.GetAccounts();
            System.debug(JSON.serializePretty(response));
        } catch (ExternalService.HerokuAPI.GetAccounts_ResponseException ex) {
            System.debug('FAILED!: ' + ex.responseCode + ' ' + ex.defaultResponse);
        }
    }
}

Asynchronous invocation:

public class InvokeHerokuAPI {
      public static void postUnitOfWork() {
        try {
            ExternalService.HerokuAPI_UnitOfWork_IN_body body = new ExternalService.HerokuAPI_UnitOfWork_IN_body();
            body.accountName = 'Heroku Integration' + Datetime.now().getTime();
            body.lastName = 'Smith';
            body.subject = 'New Heroku Integration Case';

            ExternalService.HerokuAPI.UnitOfWork_Request request = new ExternalService.HerokuAPI.UnitOfWork_Request();
            request.body = body;

            ExternalService.HerokuAPI HerokuAPI = new ExternalService.HerokuAPI();

            ExternalService.HerokuAPI.UnitOfWork_Response response =
                HerokuAPI.UnitOfWork(request, new UnitOfWorkCallback(), Datetime.now().addHours(1));

            System.debug(JSON.serializePretty(response));
        } catch (ExternalService.HerokuAPI.UnitOfWork_ResponseException ex) {
            System.debug('FAILED!: ' + ex.responseCode + ' ' + ex.defaultResponse);
        } catch (Exception ex) {
            System.debug(ex.getMessage());
        }
    }

    global class UnitOfWorkCallback extends ExternalService.HerokuAPI.UnitOfWork_Callback {
        global override void unitOfWorkResponse(List<UnitOfWork_unitOfWorkResponse_Callback> callbacks) {
            System.debug(JSON.serialize(callbacks));
        }
    }
}

Use Execute Anonymous from the Developer Console to invoke the InvokeHerokuAPI Apex code. From the menu, click Debug and then Execute Anonymous. Use InvokeHerokuAPI.getAccounts(); to invoke the synchronous GetAccounts API and InvokeHerokuAPI.postUnitOfWork(); to invoke the asynchronous UnitOfWork API.

Developer Console

Invoke Your Imported App Actions with Flow

See Invoke Your Imported Apps for more information.

In our example, we create a flow that calls our Heroku app to get a list of accounts when an opportunity is created or updated.

  1. On the Flows page in Salesforce Setup click New Flow.
  2. Select Start From Scratch and click Next.
  3. Select Record-Triggered Flow and click Create.
  4. In the Configure Start panel:
    1. Select Opportunity for the object.
    2. Select A record is created or updated to trigger the flow.
    3. Select None for the condition requirements.
    4. Select Actions and Related Records to optimize the flow for.
  5. Add an element under the start element and select Action.
  6. In the Search Actions panel, search for the name of your imported app (HerokuAPI) and select the Get Accounts action.
  7. Enter a name for your action such as GetAccountsInvoke.
  8. Click Save, enter a name for your flow, and click Save again.

External service flow action

Next Steps

Here’s some recommended reading:

  • Introduction to the Heroku AppLink Pilot for Developers
  • Heroku AppLink and Heroku Events Samples
  • Creating Agentforce Custom Actions with Heroku
  • Getting Started with Heroku Events (Pilot)
  • Integrating with Salesforce
  • Salesforce Help: Use Flow to Invoke External Service Actions
  • Salesforce Help: Invoke External Service Callouts Using Apex

Delete Your Connections, Imported Apps, and Add-on

Remove Your Connection

This action removes your connection to your org. Make sure no existing code references a connection before removing it.

To remove a connection from a Salesforce org, run the command:

$ heroku salesforce:disconnect -a integration-app -o applink-org
› Warning: Destructive Action
› This command will affect the app integration-app.

To proceed, type my-app or re-run this command with --confirm integration-app: integration-app
Disconnecting applink-org from ⬢ integration-app... done

To remove a connection from a Data Cloud org, run the command:

$ heroku datacloud:disconnect -a integration-app -o datacloud-sandbox
› Warning: Destructive Action
› This command will affect the app integration-app.

To proceed, type my-app or re-run this command with --confirm integration-app: integration-app
Disconnecting datacloud-sandbox from ⬢ integration-app... done

Delete Your Imported App

This action deletes your imported app from Salesforce. Make sure no existing code or resource references an app action before deleting it.

You can delete your imported app from Salesforce by clicking the arrow in the service’s Actions column, and selecting Delete. You can also click Delete on the imported app page.

Heroku Apps External Services

Remove Your Add-on

Removing the add-on removes all the connections. We recommend verifying that you’re not using the connections anywhere in the application before removing the add-on.

To remove your Heroku AppLink add-on, run the command:

$ heroku addons:destroy heroku-integration

Information & Support

  • Getting Started
  • Documentation
  • Changelog
  • Compliance Center
  • Training & Education
  • Blog
  • Support Channels
  • Status

Language Reference

  • Node.js
  • Ruby
  • Java
  • PHP
  • Python
  • Go
  • Scala
  • Clojure
  • .NET

Other Resources

  • Careers
  • Elements
  • Products
  • Pricing
  • RSS
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku Blog
    • Heroku News Blog
    • Heroku Engineering Blog
  • Twitter
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku
    • Heroku Status
  • Github
  • LinkedIn
  • © 2025 Salesforce, Inc. All rights reserved. Various trademarks held by their respective owners. Salesforce Tower, 415 Mission Street, 3rd Floor, San Francisco, CA 94105, United States
  • heroku.com
  • Legal
  • Terms of Service
  • Privacy Information
  • Responsible Disclosure
  • Trust
  • Contact
  • Cookie Preferences
  • Your Privacy Choices