> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.paradex.trade/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.paradex.trade/_mcp/server.

# Get JWT

POST https://api.testnet.paradex.trade/v1/auth

Authenticate using signed payload to get a JWT for usage in other endpoints

There are multiple valid headers required to be sent as part of this request.

For onboarding examples, refer to `go`, `java` and `python` code in [code-samples](https://github.com/tradeparadex/code-samples).

#### StarkNet Message Hash and Signature

Inspired by [EIP-712](https://eips.ethereum.org/EIPS/eip-712),
(a standard for hashing and signing typed structured data)
the encoding of an off-chain message is defined as:

`signed_data = Enc[PREFIX_MESSAGE, domain_separator, account, hash_struct(message)]`

where:

* `PREFIX_MESSAGE = "StarkNet Message"`
* `domain_separator` is defined as the `hash_struct` of the StarkNetDomain struct:
  * Struct contains: `name`, `chainId` and `version`
  * `chainId` is can be obtained calling `GET /system/config`
* `account` is the StarkNet account address
* The message to be hashed is represented as a struct
  * `hash_struct(message) = Enc[type_hash(MyStruct), Enc[param1], ..., Enc[paramN]]`
  * where `type_hash` is defined as in EIP-712 (but using `selector` instead of `keccak`)
  * More details on StarkNet - [Hash Functions](https://docs.starknet.io/architecture-and-concepts/cryptography/#hash_functions)

In case of more complex structure of object, you have to work in the spirit of EIP-712.
This json structure has 4 mandatory items: `types`, `primaryType`, `domain` and `message`.
These items are designed to be able to be an interface with a wallet.
At sign request, the wallet will display:

* `message` will be displayed at the bottom of the wallet display,
  showing clearly (not in hex) the message to sign.
  Its structure has to be in accordance with the type listed in primaryType,
  defined in types.
* `domain` will be shown above the message.
  Its structure has to be in accordance with `StarkNetDomain`.

The predefined types that you can use :

* `felt` : for an integer on 251 bits.
* `felt*` : for an array of felt.
* `string` : for a shortString of 31 ASCII characters max.
* `selector` : for a name of a smart contract function.
* `merkletree` : for a Root of a Merkle tree, calculated with the provided data.

Specification details: [Signing transactions and off-chain messages](https://github.com/argentlabs/argent-x/discussions/14)

#### Message Hash Sample Code

For a complete `message_hash` example, refer to `python` code in [code-samples](https://github.com/tradeparadex/code-samples).

> Examples:

```json
{
  "paradex-signature-expiration": 1682364556,
  "paradex-starknet-account": "0x129f3dc1b8962d8a87abc692424c78fda963ade0e1cd17bf3d1c26f8d41ee7a",
  "paradex-starknet-signature": [
    "1381323390094460587764867648394252677239485992175346764030313478865763678671",
    "396490140510115262427678549757564216013606350105112805717359873954984880589"
  ],
  "paradex-timestamp": 1681759756
}
```

Reference: https://docs.paradex.trade/api/prod/auth/get-jwt

## Request

### Headers

- `PARADEX-STARKNET-ACCOUNT` (string, required) — Starknet account
- `PARADEX-STARKNET-SIGNATURE` (string, required) — Starknet signature
- `PARADEX-TIMESTAMP` (string, required) — Timestamp when the signature was created
- `PARADEX-SIGNATURE-EXPIRATION` (string, optional) — Timestamp when signature expires (default 30 min | Max 1 week)
- `PARADEX-AUTHORIZE-ISOLATED-MARKETS` (string, optional) — Optional flag to authorize all isolated market accounts linked to the main account. When set to true, the returned JWT will grant trading access to all isolated accounts. Defaults to false if not specified

## Response

### 200

OK

- `jwt_token` (string, optional) — Authentication token

## Errors

### 400 Bad Request Error

Bad Request

- `data` (ResponsesApiErrorData, optional) — any additional data related to the error
- `error` (enum, optional) — unique immutable string identifier for specific error
  - Allowed values: `VALIDATION_ERROR`, `BINDING_ERROR`, `INTERNAL_ERROR`, `NOT_FOUND`, `SERVICE_UNAVAILABLE`, `INVALID_REQUEST_PARAMETER`, `ORDER_ID_NOT_FOUND`, `ORDER_IS_CLOSED`, `ORDER_IS_NOT_OPEN`, `INVALID_ORDER_SIZE`, `CLIENT_ORDER_ID_NOT_FOUND`, `DUPLICATED_CLIENT_ID`, `INVALID_PRICE_PRECISION`, `INVALID_SYMBOL`, `INVALID_TOKEN`, `INVALID_ETHEREUM_ADDRESS`, `INVALID_ETHEREUM_SIGNATURE`, `INVALID_STARKNET_ADDRESS`, `INVALID_STARKNET_SIGNATURE`, `STARKNET_SIGNATURE_VERIFICATION_FAILED`, `BAD_STARKNET_REQUEST`, `ETHEREUM_SIGNER_MISMATCH`, `ETHEREUM_HASH_MISMATCH`, `NOT_ONBOARDED`, `INVALID_TIMESTAMP`, `INVALID_BLOCK_EXPIRATION`, `ACCOUNT_NOT_FOUND`, `INVALID_ORDER_SIGNATURE`, `PUBLIC_KEY_INVALID`, `UNAUTHORIZED_ETHEREUM_ADDRESS`, `UNAUTHORIZED_ERROR`, `ETHEREUM_ADDRESS_ALREADY_ONBOARDED`, `MARKET_NOT_FOUND`, `ALLOWLIST_ENTRY_NOT_FOUND`, `USERNAME_IN_USE`, `GEO_IP_BLOCK`, `ETHEREUM_ADDRESS_BLOCKED`, `PROGRAM_NOT_FOUND`, `PROGRAM_NOT_SUPPORTED`, `INVALID_DASHBOARD`, `MARKET_NOT_OPEN`, `INVALID_REFERRAL_CODE`, `REQUEST_NOT_ALLOWED`, `PARENT_ADDRESS_ALREADY_ONBOARDED`, `INVALID_PARENT_ACCOUNT`, `INVALID_VAULT_OPERATOR_CHAIN`, `VAULT_OPERATOR_ALREADY_ONBOARDED`, `VAULT_NAME_IN_USE`, `VAULT_NOT_FOUND`, `VAULT_STRATEGY_NOT_FOUND`, `VAULT_LIMIT_REACHED`, `BATCH_SIZE_OUT_OF_RANGE`, `ISOLATED_MARKET_ACCOUNT_MISMATCH`, `NO_ACCESS_TO_MARKET`, `POINTS_SUMMARY_NOT_FOUND`, `ALGO_ID_NOT_FOUND`, `INVALID_DERIVATION_PATH`, `PROFILE_STATS_NOT_FOUND`, `INVALID_CHAIN`, `INVALID_LAYERSWAP_SWAP`, `INVALID_RHINOFI_REQUEST`, `INVALID_RHINOFI_QUOTE`, `INVALID_RHINOFI_QUOTE_COMMIT`, `SOCIAL_USERNAME_IN_USE`, `INVALID_OAUTH_REQUEST`, `RPI_ACCOUNT_NOT_WHITELISTED`, `SYSTEM_STATUS_POST_ONLY`, `SYSTEM_STATUS_CANCEL_ONLY`, `INVALID_MARKETING_CODE`, `INVALID_JOIN_WAITLIST_REQUEST`, `TRANSACTION_NOT_FOUND`, `OFFER_NOT_FOUND`, `MARKET_MARGIN_RESTRICTED`, `NOT_UNIQUE`, `ACCOUNT_ALREADY_REFERRED`, `ONBOARDING_PERIOD_EXPIRED`, `ONBOARDING_RATE_LIMITED`, `SUBACCOUNTS_LIMIT_EXCEEDED`, `INSUFFICIENT_MIN_CHAIN_BALANCE`, `INVALID_SUBKEY`, `TOKEN_LIMIT_REACHED`, `INVALID_TOKEN_SCOPE`, `INSUFFICIENT_TRANSFERRABLE_XP`, `TRANSFER_LIMIT_REACHED`
- `message` (string, optional) — detailed description of error and how to address it

### 401 Unauthorized Error

Unauthorized

- `data` (ResponsesApiErrorData, optional) — any additional data related to the error
- `error` (enum, optional) — unique immutable string identifier for specific error
  - Allowed values: `VALIDATION_ERROR`, `BINDING_ERROR`, `INTERNAL_ERROR`, `NOT_FOUND`, `SERVICE_UNAVAILABLE`, `INVALID_REQUEST_PARAMETER`, `ORDER_ID_NOT_FOUND`, `ORDER_IS_CLOSED`, `ORDER_IS_NOT_OPEN`, `INVALID_ORDER_SIZE`, `CLIENT_ORDER_ID_NOT_FOUND`, `DUPLICATED_CLIENT_ID`, `INVALID_PRICE_PRECISION`, `INVALID_SYMBOL`, `INVALID_TOKEN`, `INVALID_ETHEREUM_ADDRESS`, `INVALID_ETHEREUM_SIGNATURE`, `INVALID_STARKNET_ADDRESS`, `INVALID_STARKNET_SIGNATURE`, `STARKNET_SIGNATURE_VERIFICATION_FAILED`, `BAD_STARKNET_REQUEST`, `ETHEREUM_SIGNER_MISMATCH`, `ETHEREUM_HASH_MISMATCH`, `NOT_ONBOARDED`, `INVALID_TIMESTAMP`, `INVALID_BLOCK_EXPIRATION`, `ACCOUNT_NOT_FOUND`, `INVALID_ORDER_SIGNATURE`, `PUBLIC_KEY_INVALID`, `UNAUTHORIZED_ETHEREUM_ADDRESS`, `UNAUTHORIZED_ERROR`, `ETHEREUM_ADDRESS_ALREADY_ONBOARDED`, `MARKET_NOT_FOUND`, `ALLOWLIST_ENTRY_NOT_FOUND`, `USERNAME_IN_USE`, `GEO_IP_BLOCK`, `ETHEREUM_ADDRESS_BLOCKED`, `PROGRAM_NOT_FOUND`, `PROGRAM_NOT_SUPPORTED`, `INVALID_DASHBOARD`, `MARKET_NOT_OPEN`, `INVALID_REFERRAL_CODE`, `REQUEST_NOT_ALLOWED`, `PARENT_ADDRESS_ALREADY_ONBOARDED`, `INVALID_PARENT_ACCOUNT`, `INVALID_VAULT_OPERATOR_CHAIN`, `VAULT_OPERATOR_ALREADY_ONBOARDED`, `VAULT_NAME_IN_USE`, `VAULT_NOT_FOUND`, `VAULT_STRATEGY_NOT_FOUND`, `VAULT_LIMIT_REACHED`, `BATCH_SIZE_OUT_OF_RANGE`, `ISOLATED_MARKET_ACCOUNT_MISMATCH`, `NO_ACCESS_TO_MARKET`, `POINTS_SUMMARY_NOT_FOUND`, `ALGO_ID_NOT_FOUND`, `INVALID_DERIVATION_PATH`, `PROFILE_STATS_NOT_FOUND`, `INVALID_CHAIN`, `INVALID_LAYERSWAP_SWAP`, `INVALID_RHINOFI_REQUEST`, `INVALID_RHINOFI_QUOTE`, `INVALID_RHINOFI_QUOTE_COMMIT`, `SOCIAL_USERNAME_IN_USE`, `INVALID_OAUTH_REQUEST`, `RPI_ACCOUNT_NOT_WHITELISTED`, `SYSTEM_STATUS_POST_ONLY`, `SYSTEM_STATUS_CANCEL_ONLY`, `INVALID_MARKETING_CODE`, `INVALID_JOIN_WAITLIST_REQUEST`, `TRANSACTION_NOT_FOUND`, `OFFER_NOT_FOUND`, `MARKET_MARGIN_RESTRICTED`, `NOT_UNIQUE`, `ACCOUNT_ALREADY_REFERRED`, `ONBOARDING_PERIOD_EXPIRED`, `ONBOARDING_RATE_LIMITED`, `SUBACCOUNTS_LIMIT_EXCEEDED`, `INSUFFICIENT_MIN_CHAIN_BALANCE`, `INVALID_SUBKEY`, `TOKEN_LIMIT_REACHED`, `INVALID_TOKEN_SCOPE`, `INSUFFICIENT_TRANSFERRABLE_XP`, `TRANSFER_LIMIT_REACHED`
- `message` (string, optional) — detailed description of error and how to address it

## Types

### ResponsesApiErrorData

any additional data related to the error

## Examples

**Response**

```json
{
  "jwt_token": "eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJ0eXAiOiJhdCtKV1QiLCJleHAiOjE2ODE0NTI5MDcsImlhdCI6MTY4MTQ1MjYwNywiaXNzIjoiUGFyYWRleCBzdGFnaW5nIiwic3ViIjoiMHg0OTVkMmViNTIzNmExMmI4YjRhZDdkMzg0OWNlNmEyMDNjZTIxYzQzZjQ3M2MyNDhkZmQ1Y2U3MGQ5NDU0ZmEifQ.BPihIbGhnnsuPlReqC9x12JFXldpswg5EdA6tTiDQm-_UHaRz_8RfVBqWc2fPN6CzFsXTq7GowZu-2qMxPvZK_fGcxEhTp2k1r8MUxowlUIT4vPu2scCwrsyIujlCAwS"
}
```

**SDK Code**

```python
import requests
headers = {
  'Accept': 'application/json',
  'PARADEX-STARKNET-ACCOUNT': 'string',
  'PARADEX-STARKNET-SIGNATURE': 'string',
  'PARADEX-TIMESTAMP': 'string',
  'PARADEX-SIGNATURE-EXPIRATION': 'string'
}

r = requests.post('https://api.testnet.paradex.trade/v1/auth', headers = headers)

print(r.json())

```

```javascript
const headers = {
  'Accept':'application/json',
  'PARADEX-STARKNET-ACCOUNT':'string',
  'PARADEX-STARKNET-SIGNATURE':'string',
  'PARADEX-TIMESTAMP':'string',
  'PARADEX-SIGNATURE-EXPIRATION':'string'
};

fetch('https://api.testnet.paradex.trade/v1/auth',
{
  method: 'POST',

  headers: headers
})
.then(function(res) {
    return res.json();
}).then(function(body) {
    console.log(body);
});

```

```go
package main
import (
      "bytes"
      "net/http"
)

func main() {

    headers := map[string][]string{
        "Accept": []string{"application/json"},
        "PARADEX-STARKNET-ACCOUNT": []string{"string"},
        "PARADEX-STARKNET-SIGNATURE": []string{"string"},
        "PARADEX-TIMESTAMP": []string{"string"},
        "PARADEX-SIGNATURE-EXPIRATION": []string{"string"},
    }

    data := bytes.NewBuffer([]byte{jsonReq})
    req, err := http.NewRequest("POST", "https://api.testnet.paradex.trade/v1/auth", data)
    req.Header = headers

    client := &http.Client{}
    resp, err := client.Do(req)
    // ...
}

```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.testnet.paradex.trade/v1/auth")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["PARADEX-STARKNET-ACCOUNT"] = 'PARADEX-STARKNET-ACCOUNT'
request["PARADEX-STARKNET-SIGNATURE"] = 'PARADEX-STARKNET-SIGNATURE'
request["PARADEX-TIMESTAMP"] = 'PARADEX-TIMESTAMP'

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.testnet.paradex.trade/v1/auth")
  .header("PARADEX-STARKNET-ACCOUNT", "PARADEX-STARKNET-ACCOUNT")
  .header("PARADEX-STARKNET-SIGNATURE", "PARADEX-STARKNET-SIGNATURE")
  .header("PARADEX-TIMESTAMP", "PARADEX-TIMESTAMP")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.testnet.paradex.trade/v1/auth', [
  'headers' => [
    'PARADEX-STARKNET-ACCOUNT' => 'PARADEX-STARKNET-ACCOUNT',
    'PARADEX-STARKNET-SIGNATURE' => 'PARADEX-STARKNET-SIGNATURE',
    'PARADEX-TIMESTAMP' => 'PARADEX-TIMESTAMP',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.testnet.paradex.trade/v1/auth");
var request = new RestRequest(Method.POST);
request.AddHeader("PARADEX-STARKNET-ACCOUNT", "PARADEX-STARKNET-ACCOUNT");
request.AddHeader("PARADEX-STARKNET-SIGNATURE", "PARADEX-STARKNET-SIGNATURE");
request.AddHeader("PARADEX-TIMESTAMP", "PARADEX-TIMESTAMP");
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "PARADEX-STARKNET-ACCOUNT": "PARADEX-STARKNET-ACCOUNT",
  "PARADEX-STARKNET-SIGNATURE": "PARADEX-STARKNET-SIGNATURE",
  "PARADEX-TIMESTAMP": "PARADEX-TIMESTAMP"
]

let request = NSMutableURLRequest(url: NSURL(string: "https://api.testnet.paradex.trade/v1/auth")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```