# Falcon quick start (/falcon/quickstart)

> Verify your Falcon key, submit a signed Solana transaction over JSON-RPC or QUIC, then confirm the signature on a normal Solana RPC.



From a fresh API key to a confirmed signature. Everything below uses region `fra`; swap in any of the [nine Falcon metros](/falcon/regions).

| You need                                                          | Where it comes from                                            |
| ----------------------------------------------------------------- | -------------------------------------------------------------- |
| UUID API key                                                      | Onboarding                                                     |
| A tip account                                                     | [Falcon tips & transaction requirements](/falcon/tips)         |
| A region code                                                     | [Falcon regions & endpoints](/falcon/regions)                  |
| A normal Solana RPC: blockhash, optional simulation, confirmation | [Corvus Solana RPC](/solana-rpc), or whichever you already use |

```bash
export FALCON_HTTP='http://fra.falcon.wtf'
export FALCON_API_KEY='00000000-0000-0000-0000-000000000000'
```

<Steps>
  <Step>
    ### Check your API key with `getVersion`

    It takes no transaction and no tip, so a failure here can only be an access problem.

    ```bash
    curl -sS -X POST "${FALCON_HTTP}?api-key=${FALCON_API_KEY}" \
      -H 'Content-Type: application/json' \
      --data '{"jsonrpc":"2.0","id":1,"method":"getVersion"}'
    ```

    ```json
    {"jsonrpc":"2.0","result":{"solana-core":"falcon/VERSION","feature-set":null},"id":1}
    ```

    HTTP `401` with `{"code":-32000,"message":"unauthorized"}` means the `api-key` query parameter is missing, isn't a valid UUID, or isn't a live key. Falcon authenticates **before** it reads the body.
  </Step>

  <Step>
    ### Build and sign a transaction that pays the tip

    Add a System Program transfer to a tip account for at least 1,000,000 lamports, then set the fee payer and a fresh blockhash and sign. What comes out has to serialize to **1,232 bytes** or less, tip included.

    Full rules and a TypeScript example: [Falcon tips & transaction requirements](/falcon/tips).
  </Step>

  <Step>
    ### Submit it

    <Tabs items={['JSON-RPC', 'HTTP /binary', 'QUIC (Rust)']}>
      <Tab>
        ```bash
        export TX_BASE64='YOUR_SIGNED_TRANSACTION_BASE64'

        curl -sS -X POST "${FALCON_HTTP}?api-key=${FALCON_API_KEY}" \
          -H 'Content-Type: application/json' \
          --data "{
            \"jsonrpc\": \"2.0\",
            \"id\": 1,
            \"method\": \"sendTransaction\",
            \"params\": [
              \"${TX_BASE64}\",
              {\"encoding\": \"base64\", \"skipPreflight\": true}
            ]
          }"
        ```

        ```json
        {"jsonrpc":"2.0","result":"5Yv...signature...","id":1}
        ```

        `encoding` defaults to `base58`. The whole request body has to stay at or under 3,072 bytes.
      </Tab>

      <Tab>
        Post the raw serialized transaction: no envelope, no length prefix, no API key in the body.

        ```bash
        curl -sS -X POST "${FALCON_HTTP}/binary?api-key=${FALCON_API_KEY}" \
          -H 'Content-Type: application/octet-stream' \
          --data-binary @transaction.bin
        ```

        You get back `200` and the base58 signature as plain text.
      </Tab>

      <Tab>
        ```toml
        [dependencies]
        anyhow             = "1"
        falcon-client      = "0.1"
        solana-transaction = "3"
        tokio              = { version = "1", features = ["rt-multi-thread", "macros"] }
        uuid               = "1"
        ```

        ```rust
        use {falcon_client::FalconClient, solana_transaction::versioned::VersionedTransaction, uuid::Uuid};

        async fn submit(transaction: &VersionedTransaction) -> anyhow::Result<()> {
            let api_key = Uuid::parse_str(&std::env::var("FALCON_API_KEY")?)?;
            let client = FalconClient::connect("fra.falcon.wtf:5000", api_key).await?;

            client.send_transaction(transaction).await?;
            Ok(())
        }
        ```

        The client holds one persistent QUIC connection and reconnects on its own, so connect once and keep it.
      </Tab>
    </Tabs>
  </Step>

  <Step>
    ### Confirm the signature on a normal Solana RPC

    HTTP hands you the signature in the response body. QUIC and native UDP hand you nothing, so use the first signature of the transaction you signed; same value.

    ```bash
    curl -sS 'http://fra.corvus-labs.io:8899' \
      -H 'Content-Type: application/json' \
      --data '{"jsonrpc":"2.0","id":1,"method":"getSignatureStatuses",
               "params":[["5Yv...signature..."],{"searchTransactionHistory":false}]}'
    ```

    Poll until the status reaches the commitment you need, or until the blockhash expires.
  </Step>

  <Step>
    ### Handle the outcome

    | Outcome                                  | Do this                                                                                                                                               |
    | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
    | Signature lands                          | Done. Stop retrying.                                                                                                                                  |
    | No status yet, blockhash still valid     | Resend the **same signed bytes**. Falcon suppresses a duplicate only once that signature has already been delivered, so a landing-retry isn't wasted. |
    | Blockhash expired without landing        | Rebuild with a fresh blockhash and re-sign. That's a new signature; track it separately.                                                              |
    | `-32602`, or HTTP `400` on a raw route   | The transaction is invalid (size, signatures, encoding or tip). Fix and re-sign.                                                                      |
    | HTTP `429` or `SubmitError::RateLimited` | You're over your per-second allowance. The same signed bytes are fine to send later.                                                                  |
  </Step>
</Steps>

## Next

* [Submission methods](/falcon/submission): every route, option and response shape
* [Falcon native UDP](/falcon/udp): the datagram frame, byte for byte
* [Falcon errors & retries](/falcon/errors): the full code tables
