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

# Apex Authentication

> Authenticate to OrbitFlare Apex with an x-api-key header or api-key query parameter over HTTP, or a client certificate derived from your key over QUIC.

## Get an API Key

Apex is in beta and invite only. Once your account is enabled, open the [OrbitFlare dashboard](https://orbitflare.com/dashboard) and go to **Dashboard > Apex** to create a key. Each key belongs to a tier, and the tier sets your tip floor and rate limit.

<Warning>
  Treat the key like a password. Anyone who holds it can send transactions against your rate limit. It cannot spend your funds: transactions are still signed by your own keypair.
</Warning>

## HTTP: Header or Query Parameter

Over HTTP the key travels with every request. There are two ways to pass it:

| Method                               | Example                                               | Notes                                                      |
| ------------------------------------ | ----------------------------------------------------- | ---------------------------------------------------------- |
| **`x-api-key` header** (recommended) | `x-api-key: YOUR_API_KEY`                             | Keeps the key out of URLs, logs, and browser history       |
| **`api-key` query parameter**        | `http://fra.apex.orbitflare.com?api-key=YOUR_API_KEY` | For tools that only accept a URL, such as an RPC URL field |

<CodeGroup>
  ```bash Header theme={null}
  curl -s http://fra.apex.orbitflare.com \
    -H "Content-Type: application/json" \
    -H "x-api-key: $APEX_API_KEY" \
    -d '{"jsonrpc":"2.0","id":1,"method":"getTipAccounts","params":[]}'
  ```

  ```bash Query parameter theme={null}
  curl -s "http://fra.apex.orbitflare.com?api-key=$APEX_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"jsonrpc":"2.0","id":1,"method":"getTipAccounts","params":[]}'
  ```
</CodeGroup>

The same two options work on the plain HTTP routes (`/send`, `/send-bin`, `/send-batch`, `/send-bundle`).

<Warning>
  The HTTP routes are unencrypted, like other Solana senders: your API key and transactions are readable by anyone on the network path. Prefer QUIC, where the key never leaves your machine, or send HTTP only from a network you trust. Rotate the key from the dashboard if you think it was exposed.
</Warning>

`getTipAccounts`, `getVersion`, `health`, and `GET /ping` need no key. Everything that submits a transaction does.

A missing or invalid key returns JSON-RPC error `-32001`, or HTTP `401` on the plain routes. See [Errors and rate limits](/apex/errors-and-rate-limits).

## QUIC: Client Certificate

On QUIC nothing is authenticated per request, and **your API key never crosses the wire**. Instead:

1. Your client derives an ed25519 keypair from the API key.
2. It presents that public key in its TLS client certificate during the QUIC handshake.
3. The Apex endpoint maps the public key to your account. Every stream on that connection is yours from then on.

The Rust crate [`apex-sender-client`](/apex/rust-client) does all of this for you. For other languages the derivation is:

```text theme={null}
seed    = HKDF-SHA256(ikm = api_key bytes, salt = "apex-sender", info = "apex-sender-client-cert", L = 32)
keypair = ed25519 keypair from seed
```

Wrap the public key in Solana's dummy X.509 certificate, the same format validators use for TPU QUIC (`new_dummy_x509_certificate` in the `solana-tls-utils` crate). The endpoint only checks the key inside the certificate. The other certificate fields are ignored.

**Test vector:** the API key `test-api-key` derives the public key `ANPhYB8kmb2puLauuJKSX5orMk3rVT87gBWWy94F68hU`.

If the handshake fails, the endpoint closes the connection with an application error code:

| Close code | Meaning                                                                         |
| ---------- | ------------------------------------------------------------------------------- |
| `1`        | No client certificate was presented                                             |
| `2`        | The certificate's key does not match any API key                                |
| `3`        | Too many connections: at most 128 per API key and 64 per address. One is enough |

### Check Your Derived Key

The dashboard shows the client public key next to each API key. If your QUIC connection is refused, compare the two. With the Rust crate:

```rust theme={null}
use apex_sender_client::client_pubkey;

fn main() {
    let api_key = std::env::var("APEX_API_KEY").expect("APEX_API_KEY is required");
    // Prints the certificate key only, never the API key itself.
    println!("{}", client_pubkey(&api_key));
}
```

A mismatch means the key was copied incorrectly.

## Browsers

Every HTTP reply carries `Access-Control-Allow-Origin: *`, and `OPTIONS` preflight requests are answered, so you can call an Apex endpoint from browser code.

<Warning>
  An API key embedded in a public web page is visible to every visitor. For public frontends, send through your own backend, or use a key you are prepared to rotate.
</Warning>

Apex endpoints are served over plain HTTP on port 80. A page that is itself loaded over HTTPS is subject to the browser's mixed content rules, so test from the environment you plan to ship.
