Skip to content

Requests to other systems

An Oktul Code Studio script calls any HTTPS API with http.get, http.post, http.put, http.patch and http.delete:

  • through a connection that holds the credential, or
  • straight to a host an administrator approved.

fetch works too. This page also covers the Atlassian endpoints the jira and confluence clients do not reach.

Three ways out

Call Goes to Credential
http.get('crm', '/api/tickets') The connection with the key crm, its base URL plus the path Added by Code Studio after the script built the request
http.get(null, 'https://api.example.com/rates') That URL, on a host an administrator approved None, unless the script writes a header itself
fetch('https://api.example.com/rates') The same, with the Web API’s Response None

Set up a connection for anything that needs a credential, and the script never holds a token. A host with no credential, such as a public rates API, is approved under Hosts scripts may call directly in the site settings.

The verbs

const groups = await http.get('graph', '/groups', { query: { $top: 50 } });
const ticket = await http.post('crm', '/api/tickets', { reference: 'FIN-12', priority: 'high' });
await http.put('crm', `/api/tickets/${ticket.id}`, { status: 'open' });
await http.patch('crm', `/api/tickets/${ticket.id}`, { priority: 'low' });
await http.delete('crm', `/api/tickets/${ticket.id}`);
Verb Arguments
http.get, http.delete (connection, url, options?)
http.post, http.put, http.patch (connection, url, body, options?)
  • connection is a connection’s key, or null for a full URL on an approved host.
  • url is a path inside the connection’s base URL: /groups under https://graph.microsoft.com/v1.0 is sent to https://graph.microsoft.com/v1.0/groups. A full URL is accepted only when it starts with that base, so a script cannot send a connection’s credential to another host or a sibling path. The request is refused before the credential is read.
  • options.query is an object of parameters, encoded for you. A list value repeats the parameter: { id: ['1', '2'] } sends id=1&id=2.
  • options.headers are sent as written. The connection’s credential and its secret headers win over a header of the same name.
  • The answer is the parsed body: an object when the response says JSON, text otherwise, null when it is empty. A binary response, such as a PDF, arrives as base64 text.
  • A status of 400 or more throws HttpError, whose status, headers and body hold the response. Its message names the method, the URL and the status.

Bodies and headers

An object or an array is sent as JSON with content-type: application/json. A string is sent as written, with no content type, so name one:

const xml = '<order><id>17</id></order>';
await http.post('erp', '/orders', xml, { headers: { 'content-type': 'application/xml' } });

A form body, as most token endpoints and older APIs take, is a string from URLSearchParams:

const form = new URLSearchParams({ grant_type: 'client_credentials', scope: 'read' }).toString();
await http.post('legacy', '/oauth/token', form, { headers: { 'content-type': 'application/x-www-form-urlencoded' } });

Handling the answer yourself

http.request sends any method and never throws on a status, so a 404 or a 409 is an answer the script reads:

const answer = await http.request({
connection: 'crm',
method: 'GET',
url: `/api/customers/${customerId}`,
query: { expand: 'contacts' },
});
if (answer.status === 404) return { created: false };
const customer = JSON.parse(answer.body);

The answer has status, headers (lower-cased names), body as text, bodyEncoding ('base64' for a binary body) and simulated. Use it for retries too: on a 429 or a 503, read answer.headers['retry-after'], wait, and send again, as writing scripts shows.

fetch

fetch is the Web API, for a script written against it, on an approved host and with no credential added:

const response = await fetch('https://api.example.com/orders', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ reference: 'FIN-12' }),
});
if (!response.ok) throw new Error(`The order service answered ${response.status}`);
const order = await response.json();

The body is a string, URLSearchParams (sent as a form, with its content type set) or bytes (ArrayBuffer or Uint8Array). The URL must be https, and a URL with a user name or password in it is refused: put a credential in a connection.

Atlassian endpoints the jira client does not reach

jira takes paths that start with /rest/ or /jsm/, and confluence paths that start with /wiki/rest/ or /wiki/api/. Code Studio refuses any other path, because Atlassian’s Forge proxy routes the products’ REST APIs and nothing else. Everything outside that goes through a connection:

What you need How
This site through api.atlassian.com/ex/jira/<this cloud id>/… Nothing. Code Studio sends it through Forge as the run’s principal, with no token, and it does not count as an external request. Drop any code that asks auth.atlassian.com for a token first
Jira or Confluence on another site The Another Atlassian site connector, with a service account. The base URL is https://api.atlassian.com/ex/jira/<cloud id>, and paths are the product’s own, http.get('other-site', '/rest/api/3/myself')
Users, groups and the audit log of the organisation The Atlassian Administration connector, with an organisation API key: http.get('atlassian-admin', '/v1/orgs/<organisation ID>/users')
  • Out of reach: a path outside the REST APIs, or an endpoint Atlassian does not document. Only a person’s API token reached those, and Atlassian does not allow a Marketplace app to collect, send or store one.
  • Refused: /rest/api/3/uiModifications, Code Studio’s own dynamic forms path, however it is called through jira.

In the sandbox

A write never leaves a sandbox run. A POST, PUT, PATCH or DELETE through a connection or to an approved host is listed under Planned changes and answered with a simulated response:

Method Simulated answer
POST 201 with { "id": "sandbox-1", "key": "SANDBOX-1", "simulated": true }
PUT, PATCH 200 with the JSON body the script sent, or 204
DELETE 204
  • Reads (GET, HEAD, OPTIONS) go to an approved host. Through a connection, only when an administrator allowed sandbox reads on it; otherwise the read throws and says so.
  • A write’s answer is simulated. Keep what you sent rather than reading it back from the response.

Limits

A run sends at most 1,500 requests outside this site, 100 per minute of runtime, which is Forge’s budget for an app’s outbound requests. Requests to this site’s own gateway are routed through Forge and do not count. See limits.

When a request is refused

Message What to do
There is no connection named … Use a key from connections.list(), or ask an administrator to add the connection
… is not an approved host An administrator approves the host under Connections, or adds a connection for it
… is not a Jira REST path Use a path that starts with /rest/ or /jsm/, or a connection as in the table above
This request names the site with cloud id … The URL names another site’s gateway with no token. Use jira with the path after the cloud id, or correct the cloud id the script uses
Sandbox runs do not read through … Allow sandbox reads on the connection, or run in production

Common questions

How does a script call an API outside Atlassian?
With http.get, http.post, http.put, http.patch or http.delete and a connection's key, or with fetch to a host an administrator approved.
Does a failed request throw?
The verbs throw HttpError on a status of 400 or more. http.request and fetch return the answer instead.
Do requests to other systems run in the sandbox?
Reads do, where allowed. Writes never leave a sandbox run and are answered with a simulated response.

A problem or a question? Write to support@oktul.com or open a request in the Help Center (opens in a new tab). Both open a request with a reference and an SLA.