Corvus Labs

Falcon submission methods

Falcon JSON-RPC, /binary, /plaintext and QUIC reference: request shapes, size limits, options and exactly what each success response proves.

Every route takes one signed Solana transaction per request. Which one you want depends on what your client is already holding, and on whether you need an answer back.

RouteRequest bodyBody limitSuccessPreflight available
JSON-RPC sendTransactionJSON-RPC envelope, transaction as base58 or base64 string3,072 bytesJSON result = base58 signatureYes
POST /binaryRaw serialized transaction bytes1,232 bytes200, text/plain base58 signatureNo
POST /plaintextBase64 transaction text2,048 bytes200, text/plain base58 signatureNo
QUIC (falcon-client)Serialized transaction on a stream or datagram1,232 bytesOk(())No
Falcon native UDPAPI key prefix + raw transaction bytes1,232 bytes of transactionNothingNo

The decoded transaction is capped at 1,232 bytes everywhere; the larger JSON-RPC and /plaintext limits cover the envelope and the base64 expansion on top of it.

Acceptance is not confirmation

Falcon answers the moment the transaction validates and the forward is dispatched. No transport reports a forwarding failure: the outcome is discarded and you get the signature either way. Confirm it on a normal Solana RPC.

Authenticate

TransportHow the UUID is carried
All HTTP routes?api-key=YOUR_API_KEY_UUID query parameter
QUICThe SDK puts the UUID in the CN of a self-signed client certificate at connect
Native UDPThe first 16 bytes (or 36 ASCII characters) of every datagram
http://fra.falcon.wtf/binary?api-key=00000000-0000-0000-0000-000000000000

Anything that stores a full URL stores your key with it; Telemetry covers what not to record.

JSON-RPC

POST to the region root. There are two methods.

MethodReturns
sendTransactionBase58 signature string
getVersion{"solana-core":"falcon/<version>","feature-set":null}

Anything else comes back as -32601 method not found. There are no batch arrays; send a single request object.

sendTransaction parameters

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "sendTransaction",
  "params": [
    "SIGNED_TRANSACTION_BASE64",
    { "encoding": "base64", "skipPreflight": true }
  ]
}
OptionAccepted valuesDefaultNotes
encodingbase58, base64base58Any other value → -32602 unsupported encoding
skipPreflighttrue, falsetruefalse makes Falcon simulate before forwarding, which costs latency
preflightCommitmentprocessed, confirmed, finalizedunsetOnly used when skipPreflight is false
minContextSlotnon-negative integerunsetOnly used when skipPreflight is false
maxRetriesAccepted and ignored; Falcon does not re-drive submissions for you

Send a base64 string without "encoding":"base64" and you get -32602 transaction string is not valid base58.

Preflight

Set skipPreflight: false and Falcon simulates the transaction, signature verification included, against an upstream RPC before forwarding. A short server-side timeout bounds the wait.

Preflight outcomeResponse
Simulation returns an error-32002, message Transaction simulation failed: <error>, data = the full simulation result including logs and unitsConsumed
Upstream RPC errors-32603 preflight check failed: RPC node returned an error
Simulation exceeds the timeout-32603 preflight check timed out

None of those forward the transaction.

Reading responses

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

JSON-RPC errors come back with HTTP 200 and an error object. Two of them do carry a matching status: 401 with -32000 unauthorized, and 429 with -32001 rate limit exceeded. The full table is on Falcon errors & retries.

HTTP /binary

For when you're already holding serialized transaction bytes. No envelope, no length prefix, no key in the body.

curl -sS -X POST "http://fra.falcon.wtf/binary?api-key=${FALCON_API_KEY}" \
  -H 'Content-Type: application/octet-stream' \
  --data-binary @transaction.bin
PropertyValue
BodyRaw serialized transaction
Maximum body1,232 bytes; a larger body is 413 request body too large
Success200, text/plain, base58 signature
Rejections400 with the reason as plain text, e.g. transaction does not include required tip

HTTP /plaintext

Base64 transaction text, no JSON envelope. Whitespace around it is trimmed for you.

curl -sS -X POST "http://fra.falcon.wtf/plaintext?api-key=${FALCON_API_KEY}" \
  -H 'Content-Type: text/plain' \
  --data "${TX_BASE64}"
PropertyValue
BodyBase64 text
Maximum body2,048 bytes → 413 request body too large
Maximum decoded transaction1,232 bytes → 413 decoded transaction too large
Invalid base64400 invalid base64
Non-UTF-8 body400 invalid utf8
Success200, text/plain, base58 signature

Both raw routes check size, signatures and tip; neither runs preflight.

QUIC with falcon-client

The published Rust SDK handles the persistent connection, serialization, authentication and one reconnect-and-retry for you.

[dependencies]
falcon-client = "0.1"
uuid          = "1"
use {falcon_client::FalconClient, uuid::Uuid};

let api_key = Uuid::parse_str("YOUR_API_KEY_UUID")?;
let mut client = FalconClient::connect("fra.falcon.wtf:5000", api_key).await?;

client.send_transaction(&transaction).await?;

Other entry points, if you need them:

CallUse for
send_transaction(&VersionedTransaction)The normal path; the SDK serializes for you
send_transaction_payload(&[u8]) / send_transaction_bytes(Bytes)Transactions you already serialized
connect_with_bind(addr, key, local_addr)Pinning a fixed local UDP port for firewall rules
set_send_timeout(Duration)Bounding the stream-ack wait (default 100 ms, stream mode only)
is_connected()Liveness checks; the SDK reconnects on send failure anyway

Transport modes

TransportMode::Stream queues the transaction as a QUIC datagram and sends it on a bidirectional stream that comes back with a server ack.

A server rejection on the stream reaches you as a SubmitError. But if the datagram was queued and the stream path then fails without a server rejection (a timeout, a stream error), you still get Ok(()). So read Ok(()) here as "the server accepted it, or it's on the wire and the ack got lost".

TransportMode::Datagram sends one fire-and-forget QUIC datagram: no stream, no ack, no server error.

use falcon_client::TransportMode;

client.set_transport_mode(TransportMode::Datagram);
client.send_transaction(&transaction).await?;

Here Ok(()) means only that the packet was queued on your machine. The SDK does check the payload against the path MTU first and errors locally if it won't fit.

Switching modes takes effect on your next send, with no reconnect.

Delivery semantics

HTTP and QUIC stream mode acknowledge acceptance. QUIC datagram mode and native UDP acknowledge nothing.

Falcon keeps a signature-keyed cache of recent submissions and suppresses a duplicate, but only once that signature has already been delivered. That's why resending the same signed bytes while the blockhash is still valid is still the right way to chase a landing.

Don't re-sign to retry, though. A new signature is a different transaction, and it can land independently of the first.

On this page