Skip to content

Ethereum Action Specification

The Ethereum Action specification defines a standardized protocol for requesting and receiving Ethereum transaction or message parameters. It consists of six key components in a request/response flow:"

  1. Action Link Scheme
  2. CORS-compliant OPTIONS response
  3. GET request to the Action API
  4. GET response from the Action API
  5. POST request to the Action API
  6. POST response from the Action API

This process enables Action Clients (such as wallet applications, browser extensions, decentralized applications, or websites) to gather metadata for creating rich user interfaces and to facilitate user input to Action APIs.

Action APIs (typically implemented as website backends or servers) respond to these requests, ultimately providing transaction parameters for an Action Client to use in constructing a transaction. The Action Client then presents this constructed transaction to the user for review, approval, and signing before submission to the blockchain."

Action Link Scheme

An Action Link uses the eth-action protocol to describe an interactive request for Ethereum transaction or message parameters.

Format: eth-action:<link>

The <link> field is required as the pathname and must be an absolute HTTPS URL, conditionally URL-encoded:

  • If the URL contains query parameters, it must be URL-encoded to prevent conflicts with any Action protocol parameters.
  • If the URL doesn't contain query parameters, it should not be URL-encoded. This produces a shorter URL and a less dense QR code.

In all cases, Action Clients must URL-decode the value. This operation has no effect if the value isn't URL-encoded. If the decoded value is not an absolute HTTPS URL, the Action Client must reject it as malformed.

OPTIONS Response

To enable Cross-Origin Resource Sharing (CORS) for Action Clients, all Action APIs should respond to HTTP requests for the OPTIONS method with headers that allow clients to pass CORS checks for all subsequent requests from their origin domain.

An Action Client may perform "preflight" requests to the Action API endpoint in order to check if the subsequent GET request to the Action API will pass all CORS checks.

At a minimum, the required HTTP headers include:

  • Access-Control-Allow-Origin: * (ensures all Action Clients can safely pass CORS checks)
  • Access-Control-Allow-Methods: GET,POST,PUT,OPTIONS (ensures all required HTTP request methods are supported for Actions)
  • Access-Control-Allow-Headers: Content-Type, Authorization, Content-Encoding, Accept-Encoding

For simplicity, developers should consider returning the same response and headers to OPTIONS requests as their GET response.

GET Request

The Action Client should make an HTTP GET JSON request to the Action API endpoint with the following characteristics:

  • The request should not identify the wallet or the user.
  • The Action Client should include an Accept-Encoding header.
  • The Action Client should display the domain of the Action API endpoint as the request is being made.

GET Response

The Action API should respond with an HTTP OK JSON response (with a valid payload in the body) or an appropriate HTTP error.

The Action Client must handle HTTP client errors, server errors, and redirect responses.

The Action API should respond with:

The Action Client should:

  • Not cache the response except as instructed by HTTP caching response headers.
  • Display the title and render the icon image to the user.

GET Response Body

A GET response with an HTTP OK JSON response should include a body payload that follows this interface:

interface ActionGetResponse {
  icon: string;
  title: string;
  description: string;
  label: string;
  disabled?: boolean;
  links?: {
    actions: LinkedAction[];
  };
  error?: ActionError;
}
 
interface LinkedAction {
  href: string;
  label: string;
  parameters?: ActionParameter[];
}
 
interface ActionParameter {
  name: string;
  label?: string;
  required?: boolean;
}
 
interface ActionError {
  message: string;
}
  • icon: An absolute HTTP or HTTPS URL of an icon image (must be SVG, PNG, or WebP).
  • title: A UTF-8 string representing the source of the action request (e.g., brand, store, application, or person).
  • description: A UTF-8 string providing information about the action.
  • label: A UTF-8 string for the button text (max 5 words, starting with a verb).
  • disabled: A boolean representing the disabled state of the rendered button (default is false if not provided).
  • error: An optional error indication for non-fatal errors.
  • links.actions: An optional array of related actions for the Action API.

If links.actions is provided, the Action Client should only render buttons and input fields based on these items. If not provided, the Action Client should render a single button using the root label string.

Example GET Responses

1. Single "root" action:

This example demonstrates a simple action with no user input. The Action Client should display a single button labeled 'Claim Access Token'. When the user clicks this button, it will trigger the action without any additional input:

{
  "title": "HackerHouse Events",
  "icon": "<url-to-image>",
  "description": "Claim your Hackerhouse access token.",
  "label": "Claim Access Token"
}
2. Multiple related actions:

This example shows how to present multiple choices to the user. The Action Client should display three separate buttons: 'Vote Yes', 'Vote No', and 'Abstain from Vote'. Each button corresponds to a different voting option for the DAO proposal. When the user clicks one of these buttons, it will trigger the corresponding action:

{
  "title": "Example DAO Platform",
  "icon": "<url-to-image>",
  "description": "Vote on DAO governance proposals #1234.",
  "label": "Vote",
  "links": {
    "actions": [
      {
        "label": "Vote Yes",
        "href": "/api/proposal/1234/vote?choice=yes"
      },
      {
        "label": "Vote No",
        "href": "/api/proposal/1234/vote?choice=no"
      },
      {
        "label": "Abstain from Vote",
        "href": "/api/proposal/1234/vote?choice=abstain"
      }
    ]
  }
}
3. Actions with user input:

The following example illustrates how to offer both preset options and custom user input.

The Action Client should display:

  • Two buttons with preset amounts: 'Stake 1 ETH' and 'Stake 5 ETH'
  • A third option with a text input field where the user can enter a custom ETH amount to stake

The user can either click one of the preset buttons or enter a custom amount and click the 'Stake' button. The chosen or entered amount will be included in the request to the Action API:

{
  "title": "Staking App",
  "icon": "<url-to-image>",
  "description": "Stake ETH to help secure the Ethereum network.",
  "label": "Stake ETH",
  "links": {
    "actions": [
      {
        "label": "Stake 1 ETH",
        "href": "/api/stake?amount=1"
      },
      {
        "label": "Stake 5 ETH",
        "href": "/api/stake?amount=5"
      },
      {
        "label": "Stake",
        "href": "/api/stake?amount={amount}",
        "parameters": [
          {
            "name": "amount",
            "label": "ETH amount"
          }
        ]
      }
    ]
  }
}

The following example shows a single action that requires user input.

The Action Client should display:

  • An input field labeled 'ETH amount'
  • A 'Donate' button

The user enters the amount they wish to donate in the input field. When they click the 'Donate' button, this amount is sent to the Action API. The API can receive this amount either as part of the URL path or as a query parameter, depending on how it's configured:

{
  "icon": "<url-to-image>",
  "label": "Donate ETH",
  "title": "Donate to our charity",
  "description": "Help support this charity by donating ETH.",
  "links": {
    "actions": [
      {
        "label": "Donate",
        "href": "/api/donate/{amount}", // or /api/donate?amount={amount}
        "parameters": [
          {
            "name": "amount",
            "label": "ETH amount"
          }
        ]
      }
    ]
  }
}

POST Request

The Action Client must make an HTTP POST JSON request to the Action URL with a body payload of:

interface ActionPostRequest {
  account: string;
}
  • account: A UTF-8 string representing the Ethereum address of an account that may sign the transaction.

The Action Client should:

  • Make the request with an Accept-Encoding header.
  • Display the domain of the Action URL as the request is being made.
  • If a GET request was made, also display the title and render the icon image from that GET response.

POST Response

The Action API should respond with an HTTP OK JSON response (with a valid payload in the body) or an appropriate HTTP error.

The Action Client must handle HTTP client errors, server errors, and redirect responses. The Action API should respond with a Content-Type header of application/json.

POST Response Body

A POST response with an HTTP OK JSON response should include a body payload of:

interface ActionPostResponse {
  transaction: TransactionResponse;
  message?: string;
}
 
interface TransactionResponse {
  to: string;
  value?: string;
  data?: string;
  chainId: number;
}
  • transaction: A TransactionResponse object containing:
    • to: An Ethereum address to send the transaction to.
    • value: Wei amount to send along with the transaction (optional).
    • data: Transaction data (optional).
    • chainId: Chain ID where to call the transaction.
  • message: A UTF-8 string describing the nature of the transaction (optional).