Skip to main content

Using the Indent API

Indent provides an API for adding approvals to your applications, internal tools, and AI agents.

This API provides a general-purpose interface for requesting human review, allowing developers to secure any workflow with simple approvals:

  • Dangerous or sensitive actions like deleting a project
  • AI agents that need human review for edge cases
  • Low-code internal tools (Retool, Zapier, etc) that need human review
import { approval } from '@indent/approvals'

try {
const petition = await approval({
resources: [{ kind: 'password_reset', id: 'user_123' }]
console.log('Petition was approved, resetting password', petition)
} catch (err) {
console.error('Petition was not approved, alerting admin', err)

Try the quickstart for:

Indent Approval Kit

To install the official Python bindings, run the following command:

pip install approvals

To install the official Node.js library, run the following command in your Node.js project directory:

npm install @indent/approvals

The Indent Approval Kit is a higher-level abstraction that makes it easier to create and manage approvals. Both of these modules export an interface for creating and managing approvals.

Adding await approval

Wherever you want to add an approval, you can add await approval to your code. This will block execution until the approval is granted.

from approvals import approval, Resource

async def delete_project(user, project):
await approval({
reason="to delete project",
"resources": [Resource(kind="delete_project",]

Now, when a user requests to delete a project, their request will automatically route to the #on-call Slack channel for an engineer to review. Here's that flow in action:

sequenceDiagram autonumber User->>Indent: Request Note left of Indent: Delete Project - project_123 by user_123 Indent->>Slack: Message on-call for review Slack->>Indent: Approved Indent-->>User: Granted

How await approval works

Instead of handling requests to the Indent API directly, you can use await approval in JavaScript or Python as a control valve that blocks code execution temporarily until there's an approval.

Here's pseudo-code that describes what the function is doing under the hood:

async def approval(req, opts):
# 0. check if there is an existing petition
petition = await indent.petition.list(
# hash(requesterEmail, resourceKind, resourceId)

# 1. create a petition
if petition is None:
petition = await indent.petition.create(req)

# 2. wait for the petition to be approved
# - some petitions are approved immediately, like if on-call
# - others require human review
retries = opts.retries or 5
while petition.state.status.phase == 'open':
petition = await indent.petition.get(
await sleep(2)

# 3. return the petition if granted or raise error if closed
if petition.state.status.phase == 'granted':
return petition
raise Exception('Petition was not approved')

Making requests

With Indent, your account is called a Space since it holds all your data and configuration. Each Space has a unique identifier that we'll refer to as INDENT_SPACE that you'll use to make requests.

You can paste the command below into your terminal to make your first API request. Make sure to replace $INDENT_SPACE and $INDENT_API_TOKEN with your API key.

curl$INDENT_SPACE/resources \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $INDENT_API_TOKEN"

Indent API libraries

To make it easier to interact with the Indent API, we provide official libraries for Python and Node.js.

To install the official Python bindings, run the following command:

pip install indentapi

To install the official Node.js library, run the following command in your Node.js project directory:

npm install @indent/api

Here's an example of how to use the Node.js bindings to create a petition:

import { IndentAPI } from '@indent/api'

// using INDENT_SPACE and INDENT_API_TOKEN from env vars
const indent = new IndentAPI({
spaceName: 'acmecorp', // defaults to process.env["INDENT_SPACE"]
apiToken: '*****' // defaults to process.env["INDENT_API_TOKEN"]

await indent.petition.create({
reason: 'to complete task',
resources: [{ kind: 'action', id: 'approval+123' }]


The Indent API uses API keys for authentication. Visit your API key page to retrieve the API key you'll use in your requests.

Remember that your API key is a secret! Do not share it with others or expose it in any client-side code (browsers, native apps). Production requests must be routed through your own backend server where your API key can be securely loaded from an environment variable or key management service.

All API requests should include your API token in an Authorization HTTP header as follows:

Authorization: Bearer INDENT_API_TOKEN

There are two kinds of Indent API tokens:


As short-lived secrets (under 24 hours), user access tokens are used to make requests on behalf of a human user. They are formatted as JSON Web Token (JWT) identifying a specific user and can be used to take actions on their behalf like create, update, and delete petitions.

Why are they short-lived?

Since these tokens identify users and can be used to take action their behalf, they carry security risk that needs to be mitigated. Indent uses two-factor authentication, identity provider verification, and other controls to ensure security for users.

For long-lived API tokens, use service accounts.

Service account-based

Service account tokens are long-lived secrets used to retrieve access tokens for the service account. Once the client exchanges the refresh token for an access token, it can be used as INDENT_API_TOKEN to authorize requests. The official libraries handle this exchange for you.

curl \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $REFRESH_TOKEN"

# { "access_token": "*****", ... }

curl$INDENT_SPACE/petitions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-d '{
"petition": {
"reason": "to complete task",
"resources": [{ "kind": "action", "id": "approval+123" }],