Outbound HTTP

The outbound grant — declaring external egress per host and path, the tenant_domain template, and the honest state of enforcement.

External HTTP egress. Denied by default; one entry opens one host.

manifest.json
"permissions": [
  { "outbound": "api.stripe.com", "paths": ["/*"] },
  { "outbound": "ec.europa.eu", "paths": ["/taxation_customs/vies/rest-api/*"] },
  { "outbound": "{{tenant_domain}}", "paths": ["/api/erp/*"] }
]
FieldDescription
outboundThe allowed hostname. Supports the {{tenant_domain}} template, resolved per tenant.
pathsOptional array of allowed URL path globs. Omit to allow any path on the host.

{{tenant_domain}}

The template is what makes one manifest work for many customers. An app that talks to the merchant's own system cannot name a host at build time — every tenant has a different one. {{tenant_domain}} is resolved per tenant from that tenant's configured domain, so the declaration stays honest without becoming a wildcard:

manifest.json
{ "outbound": "{{tenant_domain}}", "paths": ["/api/erp/*"] }

On the consent screen a merchant reads it as their own domain, which is exactly the sentence they can answer.

The honest state of enforcement

outbound is declared and honoured as intent. The enforcement layer is still being built.

Seven of the platform's own apps declare real egress this way, and the declarations are accurate — they are what the consent screen shows the merchant, and they are the record of what your app talks to. What is not finished is the runtime layer that will refuse an undeclared call.

So, stated plainly:

  • A call to a host you did not declare will currently succeed. That is not permission to make one.
  • Declare your egress correctly today. It is the contract you will be held to, and it will start being enforced without a change on your side.
  • An app whose declarations are wrong will break when enforcement lands. An app whose declarations are right will not notice.

Treat the register as the truth about your app and keep it that way, and the transition costs you nothing.

Keep it narrow

{ "outbound": "*" } is not a shortcut worth taking — it makes the consent screen unanswerable, and a merchant who cannot answer it does not install you.

Narrow means two things:

One entry per host you actually call. Not the vendor's whole domain when you call one API subdomain.

A paths glob that matches the API, not the site. ["/v2/*"] says you call version two of an API. Omitting paths says you may fetch anything on that host, which is rarely what you mean.

manifest.json
{ "outbound": "api.acme-service.example", "paths": ["/v2/orders/*", "/v2/shipments/*"] }

Writing the call

An outbound call is ordinary fetch. Nothing about it is special except the declaration and your own discipline:

src/main.js
async function pushToErp(c, payload) {
  const res = await fetch(`https://${c.header('x-erp-host')}/api/erp/serials`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(payload),
    signal: AbortSignal.timeout(5000),
  });

  if (res.status >= 500) throw new HttpError(502, 'the ERP is unavailable', 'erp_unavailable');
  if (!res.ok) throw badRequest(`the ERP rejected the serial: ${res.status}`);
  return res.json();
}

Four habits that matter more here than anywhere else in your app, because the other end is not on the platform:

Set a timeout. A hanging external call becomes a hanging capability, and if that capability is on a checkout path it becomes a hanging checkout.

Do not put a third party in a synchronous path if you can avoid it. An external call inside a capability the storefront awaits means their outage is your outage. Where the work can be asynchronous, a scheduled job or an Integration Studio workflow is the better shape — a workflow gives you durable execution, retries and visibility, which hand-rolled fetch does not.

Translate their failure into your status. A 5xx from them is a 502 from you; a 4xx from them is usually a 400 with a message your caller can act on. See Errors.

Never send a platform credential outward. The context header the gateway injected is for calling the platform. It has no business in a request to api.stripe.com.

Credentials for the other end

The secret grant, which would be the natural home for an API key you need on an outbound call, is not yet proven — declarable, but used by no shipped app.

Until it is, the honest options are: a value a merchant enters in settings.json marked sensitive, or an Integration Studio credential if the integration lives in a workflow rather than in your app. Do not commit a key to the app directory, and do not put one in a manifest — the whole directory is uploaded and the manifest is published.

Next steps

Was this page helpful?