> ## Documentation Index
> Fetch the complete documentation index at: https://docs.parlayx.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication

> How a signed request becomes a trusted identity.

Every request to the ParlayX API is signed. There are no bearer tokens, no shared secrets in transit, and no way to assert an identity in a request body.

Identity comes from the key that signed the request, and from nothing else. A request cannot claim to be an actor it is not, or reach a pod its key was not minted for.

## What a key is

An API key is minted for one **actor and pod pairing**. That pairing is fixed at the moment the key is created and cannot be changed afterward.

This has a consequence worth internalizing before you write any code: **the key determines scope, so scope never appears in the request.** You do not pass a pod ID. You do not pass an actor ID. The API already knows both, because they are properties of the key that signed the call.

An actor trading two pods holds two keys and chooses which one to sign with.

## Key material

Keys are Ed25519 keypairs generated in your environment. You register the public key with ParlayX; the private key never leaves your systems and we never see it.

<Warning>
  The private key is the credential. Anyone holding it can trade the pod it is bound to, up to that pod's balance. Store it the way you would store a venue API secret — in a secrets manager, never in source control, never in a config file that ships with your application.
</Warning>

If a key is exposed, revoke it. Revocation takes effect at the front door and is reversible. See [Actors and permissions](/actors-and-permissions).

## Signing a request

Every request carries four headers:

| Header              | Contains                                       |
| ------------------- | ---------------------------------------------- |
| `PX-Key-Id`         | The UUID of the key signing this request       |
| `PX-Timestamp`      | The time the request was signed                |
| `PX-Content-Sha256` | A SHA-256 hash of the request body             |
| `PX-Signature`      | An Ed25519 signature over the canonical string |

The sequence:

<Steps>
  <Step title="Hash the body">
    Compute the SHA-256 of the request body and put it in `PX-Content-Sha256`. For a request with no body, hash the empty string.
  </Step>

  <Step title="Build the canonical string">
    Assemble the canonical string from the request method, path, timestamp, and body hash. The exact composition is given in the API Reference — build it byte for byte, since any deviation produces a signature that will not verify.
  </Step>

  <Step title="Sign it">
    Sign the canonical string with your private key and put the result in `PX-Signature`.
  </Step>

  <Step title="Send">
    Include all four headers with the request.
  </Step>
</Steps>

The SDK does this for you. Sign manually only if you are working in a language the SDK does not cover.

## What gets checked

Two stages, in order. Everything fails closed — an ambiguous result is a rejection, never an approval.

**At the front door**, before your request body is read:

1. All four headers are present
2. The timestamp is fresh
3. The key ID is well formed
4. The key exists, and its actor and pod are loaded
5. The signature verifies against the stored public key
6. The body hash matches what was signed

Signature verification happens before the body is parsed. A request that does not verify is rejected without its contents ever being interpreted.

**Then the guard**, which asks four questions in order:

| Check                                           | If it fails                     |
| ----------------------------------------------- | ------------------------------- |
| Is the key live?                                | `REVOKED_KEY` · 401             |
| Is the actor live?                              | `REVOKED_ACTOR` · 401           |
| Is the actor still granted this pod?            | `REVOKED_DELEGATION` · 401      |
| Does the key's capability cover this operation? | `INSUFFICIENT_PERMISSION` · 403 |

Three separate revocation levers, each independent. You can revoke a single key while leaving the actor intact, revoke the actor across everything it touches, or withdraw the actor's access to one pod while leaving its other pods working.

Pass all four and the request proceeds with a verified identity: organization, pod, actor, and key.

## Timestamps

Requests carry a timestamp and are only accepted inside a narrow window, which prevents a captured request from being replayed later.

Keep your system clock synchronized. Clock drift on your side presents as authentication failures that look intermittent and are not.

## Scope is carried by the key

Because the key pins one actor and one pod, paths on the signed API never name a pod or an organization. There is no `/pods/{podId}/orders` on the public surface. There is `/orders`, and it means *your* orders, in *your* pod.

A signed key can only ever address its own scope. This is structural, not a permission check that could be misconfigured — there is no way to express a request for someone else's pod, so there is no way to accidentally grant one.

Organization-wide and pod-wide oversight lives in the console, which authenticates differently because an administrator legitimately spans many pods.

## Confirming what a key is

`whoami` returns the identity behind the key that called it:

```json theme={null}
{
  "keyId": "...",
  "actorId": "...",
  "podId": "...",
  "scope": "actor",
  "capabilities": ["trade"]
}
```

Useful in three situations: verifying your signing implementation before you build anything on top of it, confirming which pod a key is bound to when you hold several, and checking that a key is still live.

Today every API key is an actor key with trade capability. The `scope` and `capabilities` fields exist so that pod-scoped and organization-scoped read keys can be introduced without changing the response shape. Read them rather than assuming their values.

## What an actor key can do

|                                       |     |
| ------------------------------------- | --- |
| Place orders in its pod               | Yes |
| Read its own orders and fills         | Yes |
| Cancel its own orders                 | Yes |
| Read its pod's balance                | Yes |
| Read another actor's orders           | No  |
| Read another actor's position or P\&L | No  |
| Read across pods                      | No  |
| Move capital                          | No  |

Placing, tracking, and cancelling an order are one responsibility under one grant. There is no configuration in which an actor can cancel an order it cannot see, or see an order it did not place.

Capital movement is not an API capability at all. Deposits, withdrawals, and rebalancing are administrative actions. See [Deposit](/deposit) and [Rebalance](/rebalance).

## Rotation

Rotating issues a new key and retires the old one — same actor, same pod, same access, new credential.

Rotate on a schedule, when someone with access to the key leaves, and immediately if exposure is suspected. Nothing about the actor, its pod, or its open positions is affected.

## Authentication failures

| Response                       | Cause                                            | Fix                                                                                          |
| ------------------------------ | ------------------------------------------------ | -------------------------------------------------------------------------------------------- |
| 401, signature                 | The signature did not verify                     | Check the canonical string composition and that you signed with the key named in `PX-Key-Id` |
| 401, timestamp                 | Outside the accepted window                      | Synchronize your clock                                                                       |
| 401, body hash                 | `PX-Content-Sha256` does not match the body sent | Hash the exact bytes transmitted, after any serialization                                    |
| 401, `REVOKED_KEY`             | Key revoked                                      | Issue a new key                                                                              |
| 401, `REVOKED_ACTOR`           | Actor revoked                                    | Restore the actor in the console                                                             |
| 401, `REVOKED_DELEGATION`      | Actor no longer granted this pod                 | Re-grant in the console                                                                      |
| 403, `INSUFFICIENT_PERMISSION` | Operation outside the key's capability           | Check `whoami`                                                                               |

<Note>
  Signature failures are almost always the canonical string or the body hash. Call `whoami` first — it is the smallest signed request you can make, and if it succeeds your signing is correct and the problem is in how you built that specific request.
</Note>
