> For the complete documentation index, see [llms.txt](https://docs.nextblock.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.nextblock.io/api/examples/golang/quic.md).

# QUIC Transaction Submission

Use QUIC when you want to send signed transaction bytes to NextBlock with minimal transport overhead from Go.

The pattern is simple:

1. Dial a regional endpoint such as `frankfurt.nextblock.io:11100`
2. Authenticate once on a bidirectional stream
3. Reuse the same QUIC connection and open one unidirectional stream per transaction

## Example

```go
package main

import (
	"context"
	"crypto/tls"
	"fmt"
	"io"
	"net"
	"os"
	"time"

	quic "github.com/quic-go/quic-go"
)

const (
	authOK    byte = 0x00
	maxTxSize      = 1232
)

type NextblockQuicClient struct {
	conn quic.Connection
}

func Connect(ctx context.Context, serverAddr string, apiKey string) (*NextblockQuicClient, error) {
	host, _, err := net.SplitHostPort(serverAddr)
	if err != nil {
		return nil, fmt.Errorf("invalid server address: %w", err)
	}

	conn, err := quic.DialAddr(ctx, serverAddr, &tls.Config{
		ServerName: host,
		NextProtos: []string{"nb-tx/1"},
		MinVersion: tls.VersionTLS13,
	}, &quic.Config{
		KeepAlivePeriod: 15 * time.Second,
		MaxIdleTimeout:  60 * time.Second,
	})
	if err != nil {
		return nil, fmt.Errorf("quic dial failed: %w", err)
	}

	authStream, err := conn.OpenStreamSync(ctx)
	if err != nil {
		conn.CloseWithError(1, "auth stream failed")
		return nil, fmt.Errorf("open auth stream: %w", err)
	}

	if _, err := authStream.Write([]byte(apiKey)); err != nil {
		conn.CloseWithError(1, "auth write failed")
		return nil, fmt.Errorf("write api key: %w", err)
	}
	if err := authStream.Close(); err != nil {
		conn.CloseWithError(1, "auth close failed")
		return nil, fmt.Errorf("close auth stream: %w", err)
	}

	response := make([]byte, 1)
	if _, err := io.ReadFull(authStream, response); err != nil {
		conn.CloseWithError(1, "auth read failed")
		return nil, fmt.Errorf("read auth response: %w", err)
	}
	if response[0] != authOK {
		conn.CloseWithError(1, "auth rejected")
		return nil, fmt.Errorf("authentication rejected")
	}

	return &NextblockQuicClient{conn: conn}, nil
}

func (c *NextblockQuicClient) SendTransaction(ctx context.Context, rawTx []byte) error {
	if len(rawTx) > maxTxSize {
		return fmt.Errorf("transaction too large: %d", len(rawTx))
	}

	stream, err := c.conn.OpenUniStreamSync(ctx)
	if err != nil {
		return fmt.Errorf("open tx stream: %w", err)
	}

	if _, err := stream.Write(rawTx); err != nil {
		return fmt.Errorf("write tx bytes: %w", err)
	}

	return stream.Close()
}

func (c *NextblockQuicClient) Close() error {
	return c.conn.CloseWithError(0, "client closing")
}

func main() {
	ctx := context.Background()
	apiKey := os.Getenv("NEXTBLOCK_API_KEY")
	if apiKey == "" {
		panic("set NEXTBLOCK_API_KEY before running")
	}

	client, err := Connect(ctx, "frankfurt.nextblock.io:11100", apiKey)
	if err != nil {
		panic(err)
	}
	defer client.Close()

	// Replace this with the serialized bytes of your signed Solana transaction.
	// Example with solana-go: rawTx, err := tx.MarshalBinary()
	rawTx := []byte{0, 1, 2, 3}

	if err := client.SendTransaction(ctx, rawTx); err != nil {
		panic(err)
	}

	fmt.Println("transaction queued")
}
```

## What To Replace

* Swap `frankfurt.nextblock.io:11100` for your nearest region.
* Replace `rawTx` with the serialized bytes of your signed transaction.
* Keep one `NextblockQuicClient` alive and reuse it instead of reconnecting per send.

## Notes

* Send raw transaction bytes, not base64.
* Each transaction goes on its own unidirectional stream.
* QUIC does not support the extra gRPC submission flags or atomic bundle submission.
* If you already build transactions with `solana-go`, serialize the signed transaction and pass those bytes into `SendTransaction()`.

See [QUIC Transaction Submission](/api/quic-transaction-submission.md) for the full endpoint list and protocol summary.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.nextblock.io/api/examples/golang/quic.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
