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?) |
connectionis a connection’s key, ornullfor a full URL on an approved host.urlis a path inside the connection’s base URL:/groupsunderhttps://graph.microsoft.com/v1.0is sent tohttps://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.queryis an object of parameters, encoded for you. A list value repeats the parameter:{ id: ['1', '2'] }sendsid=1&id=2.options.headersare 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,
nullwhen it is empty. A binary response, such as a PDF, arrives as base64 text. - A status of 400 or more throws
HttpError, whosestatus,headersandbodyhold 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 throughjira.
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.patchorhttp.deleteand a connection's key, or withfetchto a host an administrator approved. - Does a failed request throw?
- The verbs throw
HttpErroron a status of 400 or more.http.requestandfetchreturn 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.
Related
- Connections and variables
- Writing scripts
- Other systems snippets
- Script API reference
- Sandbox and production
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.