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

# Authentication

> Authenticate with the Trigger Order API using a challenge-response JWT flow.

The Trigger Order API V2 uses a challenge-response flow to authenticate users. Your wallet signs a challenge, and the server issues a JWT token valid for 24 hours.

## Authentication Flow

```
1. Request challenge    →  POST /v2/auth/challenge
2. Sign with wallet     →  (client-side)
3. Submit signature     →  POST /v2/auth/verify  →  JWT token
4. Use JWT              →  Authorization: Bearer <token>
```

## Prerequisites

Signing challenges and transactions requires `@solana/web3.js` and `bs58`:

```bash theme={null}
npm install @solana/web3.js bs58
```

## Step 1: Request a Challenge

Request a challenge for your wallet. The `type` field selects how your wallet will sign, and accepts exactly two values:

| `type`        | Use it for                                                            | You sign with                                                                    |
| :------------ | :-------------------------------------------------------------------- | :------------------------------------------------------------------------------- |
| `message`     | Standard wallets (Phantom, Solflare, and most others)                 | `signMessage` on the returned `challenge` string                                 |
| `transaction` | Hardware wallets (Ledger, etc.) that only support transaction signing | `signTransaction` on the returned `transaction` (it is never submitted on-chain) |

Both prove wallet ownership and return the same JWT. Any other value returns a `400`.

```typescript theme={null}
const challengeResponse = await fetch('https://api.jup.ag/trigger/v2/auth/challenge', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'your-api-key',
  },
  body: JSON.stringify({
    walletPubkey: walletAddress,
    type: 'message',  // or 'transaction' for hardware wallets
  }),
});

const challenge = await challengeResponse.json();
```

**Message challenge response:**

```json theme={null}
{
  "type": "message",
  "challenge": "Sign this message to authenticate with Jupiter Trigger Order API..."
}
```

**Transaction challenge response:**

```json theme={null}
{
  "type": "transaction",
  "transaction": "Base64EncodedTransactionWithMemoInstruction..."
}
```

<Note>
  Challenges expire after 5 minutes. Request a new one if your challenge expires.
</Note>

## Step 2: Sign and Verify

Sign the challenge with your wallet, then submit the signature to receive a JWT token.

**For message signing:**

```typescript theme={null}
import bs58 from 'bs58';

// Sign the challenge message with your wallet
const encodedMessage = new TextEncoder().encode(challenge.challenge);
const signature = await wallet.signMessage(encodedMessage);

const verifyResponse = await fetch('https://api.jup.ag/trigger/v2/auth/verify', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'your-api-key',
  },
  body: JSON.stringify({
    type: 'message',
    walletPubkey: walletAddress,
    signature: bs58.encode(signature),
  }),
});

const { token } = await verifyResponse.json();
// Use this token in Authorization: Bearer <token> for all authenticated requests
```

**For transaction signing (hardware wallets):**

```typescript theme={null}
import { VersionedTransaction } from '@solana/web3.js';

const signedTx = await wallet.signTransaction(
  VersionedTransaction.deserialize(Buffer.from(challenge.transaction, 'base64'))
);

const verifyResponse = await fetch('https://api.jup.ag/trigger/v2/auth/verify', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'your-api-key',
  },
  body: JSON.stringify({
    type: 'transaction',
    walletPubkey: walletAddress,
    signedTransaction: Buffer.from(signedTx.serialize()).toString('base64'),
  }),
});

const { token } = await verifyResponse.json();
```

## Using the JWT

Include the JWT in all authenticated requests:

```typescript theme={null}
const response = await fetch('https://api.jup.ag/trigger/v2/vault', {
  headers: {
    'x-api-key': 'your-api-key',
    'Authorization': `Bearer ${token}`,
  },
});
```

## Token Lifecycle

| Property          | Value     |
| :---------------- | :-------- |
| **Challenge TTL** | 5 minutes |
| **JWT TTL**       | 24 hours  |

When a token expires, repeat the challenge-response flow to obtain a new one. There is no refresh endpoint.

## Security Notes

<Warning>
  **For integrators building user-facing applications:**

  * Never store JWT tokens in local storage. Use secure, httpOnly cookies or in-memory storage
  * Always verify the challenge content before signing. Do not blindly sign arbitrary messages
  * Implement token refresh logic to handle expiration gracefully
  * The JWT is tied to the wallet public key. Do not reuse tokens across wallets
</Warning>

### What Happens if a JWT is Leaked

The JWT grants limited access. An attacker with a leaked token can:

* **Cancel orders**: This transitions the order from `open` to `ready_to_cancel`, but does **not** withdraw funds. Withdrawal requires signing a transaction with the wallet private key.
* **Edit order parameters**: Updating trigger prices or slippage does not require transaction signing.

An attacker **cannot**:

* **Withdraw funds**: All withdrawal operations require the wallet owner to sign a transaction. The vault's funds remain secure.
* **Create new orders**: Depositing tokens requires signing a deposit transaction with the wallet.

All operations involving funds (deposits, withdrawals) require the wallet owner to sign a transaction. A leaked JWT alone cannot result in loss of funds. However, if the wallet private key is also compromised, an attacker could sign transactions and withdraw funds from the vault.
