Skip Navigation
Show nav
Heroku Dev Center
  • Get Started
  • Documentation
  • Changelog
  • Search
  • Get Started
    • Node.js
    • Ruby on Rails
    • Ruby
    • Python
    • Java
    • PHP
    • Go
    • Scala
    • Clojure
  • 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
View categories

Categories

  • Heroku Architecture
    • Dynos (app containers)
    • Stacks (operating system images)
    • Networking & DNS
    • Platform Policies
    • Platform Principles
  • Command Line
  • Deployment
    • Deploying with Git
    • Deploying with Docker
    • Deployment Integrations
  • Continuous Delivery
    • Continuous Integration
  • Language Support
    • Node.js
    • Ruby
      • Working with Bundler
      • Rails Support
    • Python
      • Working with Django
      • Background Jobs in Python
    • Java
      • Working with Maven
      • Java Database Operations
      • Working with Spring Boot
      • Java Advanced Topics
    • PHP
    • Go
      • Go Dependency Management
    • Scala
    • Clojure
  • Databases & Data Management
    • Heroku Postgres
      • Postgres Basics
      • Postgres Getting Started
      • Postgres Performance
      • Postgres Data Transfer & Preservation
      • Postgres Availability
      • Postgres Special Topics
    • Heroku Data For Redis
    • Apache Kafka on Heroku
    • Other Data Stores
  • Monitoring & Metrics
    • Logging
  • App Performance
  • Add-ons
    • All Add-ons
  • Collaboration
  • Security
    • App Security
    • Identities & Authentication
    • Compliance
  • Heroku Enterprise
    • Private Spaces
      • Infrastructure Networking
    • Enterprise Accounts
    • Enterprise Teams
    • Heroku Connect (Salesforce sync)
      • Heroku Connect Administration
      • Heroku Connect Reference
      • Heroku Connect Troubleshooting
    • Single Sign-on (SSO)
  • Patterns & Best Practices
  • Extending Heroku
    • Platform API
    • App Webhooks
    • Heroku Labs
    • Building Add-ons
      • Add-on Development Tasks
      • Add-on APIs
      • Add-on Guidelines & Requirements
    • Building CLI Plugins
    • Developing Buildpacks
    • Dev Center
  • Accounts & Billing
  • Troubleshooting & Support
  • Integrating with Salesforce
  • Extending Heroku
  • Building Add-ons
  • Building an Add-on

Building an Add-on

English — 日本語に切り替える

Last updated August 30, 2021

Table of Contents

  • Understanding the add-on life cycle
  • Before you begin: Register as an add-on partner
  • Step 1: Generate your add-on manifest
  • Step 2: Integrate with the Add-on Partner API
  • Step 3: Deploy the add-on
  • Next steps
  • Advanced integrations

This article describes the basic steps for developing an add-on for the Heroku Elements Marketplace. For more advanced add-on development topics, see Advanced integrations.

Because add-ons are cloud services, you can develop them in whatever language and framework you prefer. Add-ons are not required to run on Heroku.

The primary technical requirements for an add-on are:

  • It must be able to receive HTTPS requests.
  • It must be able to parse JSON.

This article is written assuming you’re using the Add-on Partner API v3, which is the most current and what all new integrations use. If your Add-on was created before April 2018, you’ll most likely want the legacy version of this article, which documents the high level process under v1. You can find the Add-on Partner API version your add-on is using in the Partner Portal under “Settings” -> “Provisioning API”.

Understanding the add-on life cycle

Heroku communicates with your add-on service via JSON requests sent over HTTPS to endpoints you create and operate. When a customer takes an action related to an add-on, we’ll make a request to the endpoints you operate with relevant parameters. You’ll then orchestrate the necessary changes within your infrastructure to respond to those customer requests.

Add-ons are billed from when the provisioning request is sent and pro-rated to the second. Please see Usage & Billing for more details.

Actions you must implement:

  • Provisioning a new instance of your add-on,
  • OAuth token exchange, and
  • Deprovisioning an add-on instance.

Optional actions that you may implement include:

  • Plan upgrades or downgrades,
  • Creating webhooks on the app associated with an add-on resource,
  • Implementing SSO to allow your Heroku customers to log in to a dashboard you provide, and
  • Requesting additional information about a customer or their app environment via the Platform API for Partners.

OAuth token exchange is not technically required for a basic integration, but you will be unable to use asynchronous provisioning or the Platform API for Partners without it.

Plans and many other attributes of your add-on service are configured in the Add-on Partner Portal.

Before you begin: Register as an add-on partner

First, create a free Heroku account if you do not already have one.

After you’ve created an account, visit our Partner Portal, logging in if necessary. Your access to and use of the Partner Portal is governed by the salesforce.com License and Distribution Agreement for the Heroku Elements Marketplace, and if you are domiciled in Italy, the Additional Terms for Heroku Elements Marketplace Providers. The purpose of the salesforce.com License and Distribution Agreement for the Heroku Elements Marketplace is to set up the business relationship between your company and Heroku. It covers the following:

  • Distribution of your product via the Heroku Elements Marketplace.
  • Intellectual property protection for both parties.
  • Revenue sharing terms, including payments, reporting and audits.
  • Rules for pricing changes.
  • Confidentiality, allowing your company and Heroku to share details about upcoming product features, usage metrics, etc.
  • Technical support and marketing responsibilities.

Be sure to familiarize yourself with the terms and conditions of the applicable agreements and policies before signing up.

Step 1: Generate your add-on manifest

Every add-on has an addon-manifest.json file that contains metadata, configuration, and credentials for the add-on. You create your add-on manifest with Heroku’s addons-admin CLI plugin.

First ensure you’ve installed the Heroku CLI and logged in via heroku login.

Then, install the addons-admin plugin:

$ heroku plugins:install addons-admin

You can then generate a manifest in your add-on’s root directory with the following command:

$ heroku addons:admin:manifest:generate

This will create the file addon-manifest.json in the current working directory. A manifest contains secrets and should not be checked into source control.

The generated manifest is a skeleton that includes defaults and placeholder values. You’ll need to modify some attributes in the manifest to create a working add-on service.

Edit the manifest file and set the id field to the identifier you’d like your add-on to have when developers interact with it via the CLI. This identifier will also be used in the path to your Heroku Elements page, and is immutable.

We also refer to your id as your add-on slug.

For example, if your product is called “MySQL-o-Matic”, you might specify mysqlomatic for id. Later on, you can specify a human-friendly display name and marketing copy for the Heroku Elements Marketplace through the Partner Portal.

For a comprehensive description of manifest fields, see Add-on Manifest Format.

It’s common for add-on partners to create a -staging version of their add-on to test new features “full stack”, and point this -staging add-on to staging versions of the integration infrastructure they manage. You might want to do this as you’re building a new add-on too, to give you a safe place to test as you reach integration milestones.

Step 2: Integrate with the Add-on Partner API

When a Heroku customer performs certain administrative actions related to your add-on (such as provisioning it or changing their current plan), Heroku sends requests to your registered endpoint so you can perform the necessary actions. These requests are part of the Add-on Partner API.

Your add-on must be configured to receive and handle Add-on Partner API requests appropriately. Please see the Add-on Partner API for guidelines on SSL, idempotency, request body validation, versioning headers, and how to handle errors.

Basic authentication

Requests from Heroku for the Add-on Partner API use basic authentication over HTTPS for all operations. You’ll need to build basic authentication into your add-on integration endpoint. The username that Heroku sends will be the id in your manifest (the identifier you chose above) and the password will be the password field set in your manifest.

use Rack::Auth::Basic, "Heroku API" do |username, password|
  Rack::Utils.secure_compare(::Digest::SHA256.hexdigest(username), ::Digest::SHA256.hexdigest("<id from manifest>")) &
  Rack::Utils.secure_compare(::Digest::SHA256.hexdigest(password), ::Digest::SHA256.hexdigest("<password from manifest>"))
end

The provisioning request

When a customer makes a request to provision your add-on, Heroku sends a POST request with a JSON payload to the /heroku/resources endpoint at the domain you configured in your add-on manifest.

We frequently refer to add-on instances as a resource.

When an add-on receives a provisioning request from Heroku, it must:

  • Respond with a successful HTTP status code,
  • Persist relevant metadata needed to identify an add-on resource uniquely,
  • Provision resources,
  • Exchange OAuth tokens to allow for secure scoped Platform API for Partners access, and
  • Update us with relevant configuration values that will be included in the owning app’s environment.

This can be done asynchronously or synchronously at your discretion, which may depend on how long your provisioning process takes.

Example request

# POST /heroku/resources

{
  "callback_url": "https://api.heroku.com/addons/01234567-89ab-cdef-0123-456789abcdef",
  "name": "acme-inc-primary-database",
  "oauth_grant": {
    "code": "01234567-89ab-cdef-0123-456789abcdef",
    "expires_at": "2016-03-03T18:01:31-0800",
    "type": "authorization_code"
  },
  "options": { "foo" : "bar", "baz" : "true" },
  "plan": "basic",
  "region": "amazon-web-services::us-east-1",
  "uuid": "01234567-89ab-cdef-0123-456789abcdef",
  ... Some fields omitted
}

Example implementation psuedocode

The example responses below assume you’re using asynchronous provisioning. Some processes can be combined and requests omitted under synchronous provisioning.

post '/heroku/resources' do
  # The OAuth client secret for your add-on service can be retrieved from the partner
  # portal and should be stored encrypted at rest.
  oauth_client_secret = config.get('oauth_client_secret')

  # Create an instance of your add-on for the app uuid in the request
  resource = Resource.create!(
    plan: json_params[:plan],
    region: json_params[:region],
    oauth_grant_code: json_params[:oauth_grant][:code],
    oauth_grant_expires_at: parse_time(json_params[:oauth_grant][:expires_at]),
    oauth_grant_type: json_params[:oauth_grant][:type],
    heroku_uuid: json_params[:uuid] # The resource uuid used in all related requests
  )
  # Exchanging tokens could be done in a background job
  access_token_response = ExchangeGrantCode.exchange(
    grant_code: resource.oauth_grant_code,
    oauth_client_secret: oauth_client_secret
  )
  response = {
    # Unique identifier, which can (and probably should) be the UUID we provide
    id: resource.heroku_uuid,
    message: "Your add-on is being provisioned. It will be available shortly."
  }

  status 202
  response.to_json
end

Example response

{
  "id": "37136775-c6f6-4d29-aa6b-12d1dad4febb",
  "message": "Your add-on is being provisioned. It will be available shortly."
}

You still need to update the configuration value and mark provisioning as complete to complete the asynchronous provisioning process.

The deprovisioning request

When a customer removes your add-on, Heroku sends a DELETE request to your registered integration endpoint. This request uses HTTPS and HTTP Basic Authentication just like the provisioning request. See the deprovision section of the Add-on Partner API reference article for more details.

The DELETE request does not include a request body. You should parse the heroku_uuid from the URL.

Example request

DELETE /heroku/resources/:heroku_uuid HTTP/1.1
Host: <your-addon.example.com>:443
Authorization: Basic YWRkb24tc2x1ZzpzdXBlci1zZWNyZXQ=
Content-Type: application/json
Accept: application/vnd.heroku-addons+json; version=3

Example implementation

delete '/heroku/resources/:heroku_uuid' do
  Resource.find_by(heroku_uuid: params[:heroku_uuid]).destroy
  status 204
end

Example response

Return a 204 No Content response to let us know you’ve successfully received and processed the request.

The plan change request (upgrade / downgrade)

When a customer changes their current add-on plan, Heroku sends a PUT request to your add-on integration API endpoint. This request includes the plan slug the Heroku customer wants to change to.

When your add-on receives a plan change request, it should:

  • Upgrade or downgrade any features and settings accordingly,
  • Automatically migrate any necessary state - for instance, database contents. If this isn’t possible, be sure to document thoroughly how a customer can do this manually in your add-on’s Dev Center documentation.
  • Respond with any new configuration values to use for the new plan.

Example request

PUT /heroku/resources/:heroku_uuid HTTP/1.1
Host: <your-addon.example.com>:443
Authorization: Basic YWRkb24tc2x1ZzpzdXBlci1zZWNyZXQ=
Content-Type: application/json
Accept: application/vnd.heroku-addons+json; version=3

Example implementation (Ruby)

put '/heroku/resources/:heroku_uuid' do
  resource = Resource.find_by(heroku_uuid: params[:heroku_uuid])
  original_plan = resource.plan
  resource.change_plan(json_params[:plan])

  result = {
    config: {
      ADDON_SLUG_URL: resource.url
    },
    message: "Successfully changed from #{original_plan} to #{resource.plan}"
  }

  result.to_json
end

Example response

{
  "config": {
    "ADDON_SLUG_URL": "https://user:password@service.example.com/new-url"
  },
  "message": "Successfully changed from original_plan to another_plan"
}

Single Sign On

Implementing a single sign-on (SSO) integration lets you provide a web UI control panel to your add-on customers. Please see our in-depth guide to implementing SSO.

Step 3: Deploy the add-on

Once your service is working properly, deploy it somewhere that is publicly accessible. It does not have to be deployed to Heroku (but it certainly can be).

When you’ve deployed the add-on, modify your addon-manifest.json file’s base_url and sso_url values within the production attribute to point to your deployed app.

Finally, push your updated addon-manifest.json file up to Heroku in order to register your add-on. You need to be logged in to the Heroku CLI with the account that accepted the Partner Terms of Service for the add-on.

$ heroku addons:admin:manifest:push

At this point, your add-on is available for you (and only you) to provision. You can manage its details in the Partner Portal and install it via the Heroku Dashboard or CLI.

Next steps

Please see Bringing an Add-on to Market for information about managing plans and other topics related to releasing an add-on in the Heroku Elements Marketplace.

Advanced integrations

There is far more that you can do with your integration to Heroku. Check out some of the articles below to explore more advanced integrations.

  • Shareable Add-ons
  • Platform API for Partners
  • Add-on Logging Integration
  • CLI Parameter Handling

Next: Bringing an Add-on to Market

Keep reading

  • Building Add-ons

Feedback

Log in to submit feedback.

What Is an Add-on? How Add-ons Work

Information & Support

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

Language Reference

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

Other Resources

  • Careers
  • Elements
  • Products
  • Pricing

Subscribe to our monthly newsletter

Your email address:

  • RSS
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku Blog
    • Heroku News Blog
    • Heroku Engineering Blog
  • Heroku Podcasts
  • Twitter
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku
    • Heroku Status
  • Facebook
  • Instagram
  • Github
  • LinkedIn
  • YouTube
Heroku is acompany

 © Salesforce.com

  • heroku.com
  • Terms of Service
  • Privacy
  • Cookies
  • Cookie Preferences