> For the complete documentation index, see [llms.txt](https://docs.p0.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.p0.dev/getting-started/automate-access-requests-with-the-api.md).

# Automate access requests with the API

This guide shows you how to drive P0 just-in-time access from your own code. You use the [Command API](/access-management/just-in-time-access/just-in-time-api/command-api.md) to create an access request, check its status, and then use the [Access Requests API](/access-management/just-in-time-access/just-in-time-api/access-requests-api.md) to approve, deny, or revoke it.

Automating these steps lets you wire P0 into bots, CI/CD pipelines, and internal security tooling — for example, requesting a short-lived role during a deployment, or auto-approving access when an alert fires. Every request still runs through your organization's access policies, guardrails, and audit trail, exactly as it does in the dashboard.

## Prerequisites

Before you begin, confirm the following:

* **A P0 organization** — You know your organization slug (`orgId`), shown in the P0 dashboard URL and in the CLI as your org name.
* **A P0 API token** — You have a token, stored securely, belonging to an identity in your P0 organization. Approving and denying also require that identity to be an approver under your access policies. See [Authenticating with the P0 API](/getting-started/authenticating-with-the-p0-api.md) for how to get a token.
* **A configured resource integration** — At least one integration (AWS, Google Cloud, Azure, SSH, and so on) is installed, so there is something to request. See [Resource integrations](/integrations/resource-integrations.md).
* **A JSON tool** — The examples use [`curl`](https://curl.se/) and [`jq`](https://jqlang.github.io/jq/) to send requests and read responses.

{% hint style="info" %}
A token carries the P0 role of the identity behind it. An Owner can revoke any grant, but approving and denying requests follow your [access policies](/access-management/just-in-time-access/access-policies.md) rather than the Owner role, and any member of your organization can create a request. In production, route approvals through your access policies rather than approving every request with the same automation identity.
{% endhint %}

## Set your base URL and authentication

Every endpoint lives under your organization's base URL and authenticates with a bearer token. To get a token, see [Authenticating with the P0 API](/getting-started/authenticating-with-the-p0-api.md).

```bash
export P0_ORG="your-org-slug"
export P0_API_TOKEN="your-token"
export P0_BASE_URL="https://api.p0.app/o/${P0_ORG}"
```

Include the token in the `Authorization` header on every request:

```bash
curl -H "Authorization: Bearer ${P0_API_TOKEN}" "${P0_BASE_URL}/..."
```

## Create an access request

Send a `POST` request to the Command API at `/o/{orgId}/command`. The body has two fields:

| Field        | Type             | Description                                                                                                                                |
| ------------ | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `argv`       | array of strings | The request arguments, matching the [`p0 request`](/p0-cli/p0-commands-and-usage/p0-request.md) CLI command with the leading `p0` removed. |
| `scriptName` | string           | The client name to record. Use `"p0"`.                                                                                                     |

The `argv` array mirrors the CLI exactly. The command `p0 request aws role MyReadOnlyRole --account 123456789012 --reason "..."` becomes the array below:

```bash
curl -s -X POST "${P0_BASE_URL}/command" \
  -H "Authorization: Bearer ${P0_API_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "argv": [
      "request", "aws", "role", "MyReadOnlyRole",
      "--account", "123456789012",
      "--reason", "Investigating S3 access issues"
    ],
    "scriptName": "p0"
  }'
```

The response confirms that P0 created the request and returns its ID in the `id` field. Capture the ID for the next steps:

```bash
REQUEST_ID=$(curl -s -X POST "${P0_BASE_URL}/command" \
  -H "Authorization: Bearer ${P0_API_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "argv": ["request", "aws", "role", "MyReadOnlyRole", "--account", "123456789012", "--reason", "Automated deploy access"],
    "scriptName": "p0"
  }' | jq -r '.id')

echo "Created request ${REQUEST_ID}"
```

{% hint style="info" %}
To find the right `argv` for any resource, run the equivalent CLI command with `--help` — for example `p0 request gcloud --help`. See [`p0 request`](/p0-cli/p0-commands-and-usage/p0-request.md) for the full list of providers and subcommands. For the complete request and response schema, see the [Command API reference](/access-management/just-in-time-access/just-in-time-api/command-api.md).
{% endhint %}

## Check the request status

To read the current state of a request from a script, send a `GET` request to `/o/{orgId}/permission-requests/{requestId}`. It returns the full request document as plain JSON:

```bash
curl -s "${P0_BASE_URL}/permission-requests/${REQUEST_ID}" \
  -H "Authorization: Bearer ${P0_API_TOKEN}" | jq
```

Poll this endpoint to wait for a decision — for example, until the status moves from pending to approved and provisioned.

{% hint style="info" %}
The `/command/{requestId}/poll` endpoint streams live updates over Server-Sent Events for interactive clients such as the CLI and web app. For one-shot status checks in automation, use the `GET /permission-requests/{requestId}` endpoint shown above.
{% endhint %}

## Approve, deny, or revoke the request

The Access Requests API acts on an existing request by ID. Each action is a `POST` to `/o/{orgId}/permission-requests/{requestId}/{action}`, where `{action}` is `approve`, `deny`, or `revoke`.

Approve a request:

```bash
curl -s -X POST "${P0_BASE_URL}/permission-requests/${REQUEST_ID}/approve" \
  -H "Authorization: Bearer ${P0_API_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{}'
```

Each action returns a success confirmation:

```json
{ "message": "Success" }
```

When you approve a request, you can override the grant duration with an optional body. Set `expirationLength` to a P0 duration such as `30m`, `2h`, or `1d`, and set `isCustomExpiry` to `true`:

```bash
curl -s -X POST "${P0_BASE_URL}/permission-requests/${REQUEST_ID}/approve" \
  -H "Authorization: Bearer ${P0_API_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{ "expirationLength": "2h", "isCustomExpiry": true }'
```

Deny a pending request, or revoke an active grant before it expires, by changing the action:

```bash
# Deny a pending request
curl -s -X POST "${P0_BASE_URL}/permission-requests/${REQUEST_ID}/deny" \
  -H "Authorization: Bearer ${P0_API_TOKEN}" -H "Content-Type: application/json" -d '{}'

# Revoke an active grant
curl -s -X POST "${P0_BASE_URL}/permission-requests/${REQUEST_ID}/revoke" \
  -H "Authorization: Bearer ${P0_API_TOKEN}" -H "Content-Type: application/json" -d '{}'
```

{% hint style="warning" %}
P0 auto-revokes access at expiry, so you only need `revoke` to end a grant early. Denying or revoking a request cannot be undone — submit a new request to restore access.
{% endhint %}

## Verify it worked

Confirm the full lifecycle end to end:

1. **Check the status.** Send a `GET` to `/permission-requests/{requestId}` and confirm the state reflects your action (approved, denied, or revoked).
2. **Confirm provisioning.** For an approved request, verify the underlying grant exists — for example, assume the AWS role with [`p0 aws role assume`](/p0-cli/p0-commands-and-usage/p0-aws-role-assume.md), or check the resource directly.
3. **Review the audit trail.** Each action writes an audit event (`api.jit.permission-requests.approved`, `.denied`, or `.revoked`). Find it in the P0 dashboard or in your [SIEM integration](/integrations/siem-integrations.md).

## Troubleshooting

| Symptom                           | Cause                                                         | Fix                                                                                                                                                                                                                                     |
| --------------------------------- | ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `401 Unauthorized`                | Missing, invalid, or expired token                            | Confirm the `Authorization: Bearer` header is set and the token is valid. The recommended Google Cloud and CLI tokens are short-lived, so mint a fresh one if it may have expired.                                                      |
| `403 Forbidden`                   | The token's identity lacks permission for this action         | Confirm the identity is a member of your P0 organization. For approve and deny, it must also be an approver under the matching [access policy](/access-management/just-in-time-access/access-policies.md). Owners can revoke any grant. |
| `404 Not Found` on an action      | Wrong `requestId` or `orgId`, or the request no longer exists | Recheck the ID returned when you created the request and confirm `P0_ORG` matches your organization slug.                                                                                                                               |
| `argv is not an array of strings` | The `argv` field is missing or malformed                      | Send `argv` as a JSON array of strings, and put each flag and its value as separate elements.                                                                                                                                           |
| Request stays pending             | An approval policy requires a human approver                  | Approve it with the Access Requests API, or adjust the matching [access policy](/access-management/just-in-time-access/access-policies.md).                                                                                             |

## Related

* [Command API](/access-management/just-in-time-access/just-in-time-api/command-api.md) — create access requests programmatically.
* [Access Requests API](/access-management/just-in-time-access/just-in-time-api/access-requests-api.md) — approve, deny, and revoke requests.
* [Authenticating with the P0 API](/getting-started/authenticating-with-the-p0-api.md) — get a token to authenticate these requests.
* [`p0 request`](/p0-cli/p0-commands-and-usage/p0-request.md) — the CLI command whose arguments the `argv` array mirrors.
* [Management API](/p0-management/management-api.md) — programmatically configure roles and JIT settings.
