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

# JWT generation example

> Working Python script that constructs the required claims and signs an ES256 JWT for Bastion API request signing, including body hashing and headers.

If you prefer to generate JWTs programmatically within your Python application or tooling, you can use the following lightweight script. It constructs the required JWT claims and signs the token using your ES256 private key.

## Step 1: Generate your private key

Before running the script, securely generate your ES256 private and public keys. For testing purposes you can use this command:

<CodeGroup>
  ```shell theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  openssl ecparam -name prime256v1 -genkey -noout -out es256-private.pem
  openssl ec -in es256-private.pem -pubout -out es256-public.pem
  ```
</CodeGroup>

## Step 2: Script to generate a JWT

<CodeGroup>
  ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  import jwt
  import json
  import datetime
  import hashlib

  # Required inputs
  method = "POST"
  url = "/v2/accounts"
  body = "{\n \"request_id\": \"2de34f56-d549-5651-af61-e2316bc3c47a\",\n \"identity_id\": \"2zwoDFH1yR9ofx1DFHqNDNL2aFS\"\n}"

  # Loaded EC private key object (ES256)
  # This assumes PRIVATE_KEY is already loaded

  hashedBody = hashlib.sha256(body.encode()).hexdigest()

  # Generate timestamp
  timestamp = datetime.datetime.now(datetime.UTC).timestamp()

  # Construct claims
  claims = {
      "iat": int(timestamp),
      "req-method": method,
      "req-path": url,
      "body": hashedBody
  }

  # Sign JWT using ES256
  token = jwt.encode(claims, PRIVATE_KEY, algorithm="ES256")
  ```
</CodeGroup>

The resulting `token` variable contains the signed JWT string, which you can include in your API request header like so:

```http theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
bastion-signature: <your-signed-jwt>
```

## Notes

* The `iat` claim must reflect the current UTC time and be within 30 seconds of server time.
* The `body` must match the exact order and structure of the request body.
* The script does not skip the `body` claim for `GET` or `HEAD`


## Related topics

- [Request signing with JWTs](/v2/api-reference/authentication/request-signing.md)
- [API authentication overview](/guides/security/api-authentication.md)
- [Wallet key management](/guides/security/key-management.md)
- [Product updates](/changelog.md)
- [Sandbox vs. production environments](/guides/getting-started/sandbox-vs-production-environments.md)
