flightbyte

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 15, 2026 License: MIT Imports: 17 Imported by: 0

README

FlightByte

FlightByte for Go

The official Go client for the FlightByte flight status API.
Look flights up, watch them with signed webhooks and verify every delivery,
from a web service, a worker or a CLI.

Go reference No dependencies MIT license

Docs · Get a free key · API reference · Webhook docs · Support


Quick start

  1. Get a free API key at app.flightbyte.dev/signup.

  2. Add the module:

    go get github.com/FlightByte-dev/flightbyte-go
    
  3. Look a flight up:

    package main
    
    import (
    	"context"
    	"fmt"
    	"log"
    	"os"
    
    	flightbyte "github.com/FlightByte-dev/flightbyte-go"
    )
    
    func main() {
    	client, err := flightbyte.NewClient(os.Getenv("FLIGHTBYTE_API_KEY"))
    	if err != nil {
    		log.Fatal(err)
    	}
    
    	flight, err := client.Flights.Get(context.Background(), flightbyte.FlightParams{
    		Carrier:      "TK",
    		FlightNumber: "1848",
    		Date:         "2026-09-08", // local departure date at the origin
    	})
    	if err != nil {
    		log.Fatal(err)
    	}
    
    	fmt.Println(flight.Status.Label)                     // Scheduled
    	fmt.Println(flight.Departure.Gate)                   // A11
    	fmt.Println(flight.Departure.Times.BestGate().Local) // 2026-09-08T12:25:00.000
    }
    

flightbyte.FormatDate(time.Time) turns a date into the YYYY-MM-DD the API expects. To try it from a terminal, run the bundled example:

export FLIGHTBYTE_API_KEY=frk_live_...
go run github.com/FlightByte-dev/flightbyte-go/examples/lookup@latest TK 1 2026-11-11
TK1:2026-11-11@SAW-AMS  SAW → AMS
Status     Scheduled (scheduled)
Departure  2026-11-11T10:40:00.000 +03, gate -
Arrival    2026-11-11T12:35:00.000 CET, baggage -
Quota      23 of 30 left this minute

TK1 flies two legs that day, and without an Origin the API picked one; flight.Alternates lists both.

What you can do

You want to You call
Show a passenger their gate and delay client.Flights.Get(ctx, ...) returns the live snapshot: phase, gate, terminal, baggage belt, scheduled, estimated and actual times, delay, aircraft and codeshares.
Look up TK1 tomorrow, which flies two legs Set Origin: "IST". Without it the flight lists every leg in Alternates, and a subscription refuses to guess: the *APIError carries the Legs to choose from.
Be told when anything changes client.Subscriptions.Create(ctx, ...) starts the watch and returns the signing secret, shown once.
Find out why webhooks stopped arriving client.Subscriptions.Get(ctx, id) and Deliveries(ctx, id, 0) show the status and the HTTP code and error of every recent attempt.
Trust a webhook on your server flightbyte.WebhookHandler(secret, fn) verifies the signature against the raw body, decodes the event and calls you.

API

Flights: read-only.

Method What it does
Flights.Get Live status of one flight leg, and every leg the number flies that day.
GET /v1/flights/{carrier}/{flightNumber}/{date}
Flights.Timeline The airline's own change history for a leg, newest first.
GET /v1/flights/{carrier}/{flightNumber}/{date}/timeline

Subscriptions

Method What it does Access
Subscriptions.Create Watch a flight and get a signed webhook for every change. Counts as a watched flight on your plan; set IdempotencyKey so a retry never counts twice.
POST /v1/subscriptions
writes
Subscriptions.List One page of your subscriptions, filtered by status, carrier, number or date.
GET /v1/subscriptions
read-only
Subscriptions.All Every matching subscription as an iter.Seq2, fetched page by page as you range over it.
GET /v1/subscriptions
read-only
Subscriptions.Get One subscription with its status and delivery counters.
GET /v1/subscriptions/{id}
read-only
Subscriptions.Update Change the webhook URL, event filter, delay threshold, headers or metadata.
PATCH /v1/subscriptions/{id}
writes
Subscriptions.Pause Stop polling and deliveries without losing anything.
POST /v1/subscriptions/{id}/pause
writes
Subscriptions.Resume Resume a paused or auto-suspended subscription.
POST /v1/subscriptions/{id}/resume
writes
Subscriptions.Delete Delete a subscription for good.
DELETE /v1/subscriptions/{id}
destructive
Subscriptions.SendTest Send a signed subscription.test webhook to the subscription's URL.
POST /v1/subscriptions/{id}/test
writes
Subscriptions.Deliveries Recent delivery attempts with HTTP status and errors.
GET /v1/subscriptions/{id}/deliveries
read-only
Subscriptions.Events Recent events with the exact payloads that were sent.
GET /v1/subscriptions/{id}/events
read-only
Subscriptions.Secret Reveal the signing secret again.
GET /v1/subscriptions/{id}/secret
read-only
Subscriptions.RotateSecret Generate a new signing secret. Previous signatures stop validating.
POST /v1/subscriptions/{id}/secret/rotate
writes

Countries: read-only.

Method What it does
Countries.List Every country with its name in any language, emoji and flag URLs.
GET /v1/countries
Countries.Get One country by its ISO 3166-1 alpha-2 code.
GET /v1/countries/{code}
Countries.FlagURL The URL of a country's SVG flag, 4:3 or square. Needs no key, so it can go straight into an <img>.
GET /v1/flags/{code}.svg

Every method takes a context.Context first. Struct fields carry the API's own names in Go form (Carrier, FlightNumber, Date, Origin, WebhookURL, IdempotencyKey, …), and the models carry every field of the API's JSON. The full snapshot is documented under Flights.

Watch a flight

created, err := client.Subscriptions.Create(ctx, flightbyte.CreateSubscriptionParams{
	Carrier:      "TK",
	FlightNumber: "1848",
	Date:         "2026-09-08",
	WebhookURL:   "https://api.example.com/hooks/flights",
	Events: []flightbyte.FlightChangeType{
		flightbyte.ChangeDepartureGate,
		flightbyte.ChangeDelayIncreased,
		flightbyte.ChangeDeparted,
	},
	Metadata:       map[string]string{"bookingId": "BK-1042"},
	IdempotencyKey: "BK-1042", // safe to retry
})
if err != nil {
	return err
}

// Shown in full only now: store it where your webhook receiver can read it.
signingSecret := created.Secret

FlightByte only delivers to public HTTPS addresses. To receive on your machine, expose the port with a tunnel (cloudflared tunnel --url http://localhost:8080 or ngrok http 8080) and use that URL. Subscriptions.SendTest(ctx, id) sends a signed subscription.test whenever you want to see the path work.

Receive webhooks

A receiver has to do four things, in this order: verify the signature against the raw body, deduplicate on the event id, answer 2xx before doing the work, and branch on the event type. WebhookHandler does the first and gives you an ordinary http.Handler:

mux := http.NewServeMux()
mux.Handle("POST /hooks/flights", flightbyte.WebhookHandler(signingSecret,
	func(ctx context.Context, event *flightbyte.WebhookEvent) error {
		// A retry carries the same id: answer it, but don't handle it twice.
		if !markHandled(event.ID) {
			return nil
		}
		// Answer first: FlightByte treats a delivery as failed after 10 seconds.
		jobs <- event
		return nil
	}))

It answers 401 for a bad signature, 400 for a body that is not an event, 500 when your function returns an error (so FlightByte retries) and 200 otherwise. An error from your function is logged with the log package; pass flightbyte.WithErrorHandler(fn) to send it somewhere else. WebhookHandler panics when the secret is empty, because anyone can sign with an empty key: an unset environment variable fails at startup instead of accepting forged events. Then, off the request path:

for event := range jobs {
	switch event.Type {
	case flightbyte.EventFlightUpdated:
		for _, change := range event.Changes {
			log.Printf("%s %s: %s", event.FlightKey, change.Type, change.Message)
		}
	case flightbyte.EventSubscriptionCompleted:
		log.Printf("%s finished", event.FlightKey)
	}
}

With another router, read the body yourself and call flightbyte.ConstructWebhookEvent(payload, r.Header.Get(flightbyte.SignatureHeader), secret). A runnable receiver is in examples/webhook-server.

How the signature works
x-flightbyte-signature: t=1789305971,v1=5f1c9a...e3b2
v1 = HMAC-SHA256(secret, "<t>." + <raw body>)

VerifyWebhook computes the HMAC over the body you received, not over JSON you decoded and encoded again, compares it in constant time with hmac.Equal, accepts any of several v1 values, and rejects a t more than five minutes from now. SignWebhook produces the same header, for testing your receiver. The test suite checks both against signatures made by the API's own signer, and fuzzes the header parser.

The reasons behind each step are in Verifying webhook signatures, and reference receivers in Node.js, Python and Go are in flightbyte-webhook-examples.

Errors

A failed request returns an *APIError that matches a sentinel with errors.Is, so you branch on the status without comparing numbers, and read the details with errors.As.

Sentinel Status When, and what to do
ErrBadRequest 400 Invalid input; Messages has one line per field. For a number that flies twice, Legs lists them: set one's Origin.
ErrUnauthorized 401 Missing, invalid, revoked or expired API key.
ErrPaymentRequired 402 No watched flights left this period. Code says why (watched_flight_limit or overage_cap_reached); ResetsAt says when the count resets.
ErrForbidden 403 The account is suspended, or an OAuth token lacks the scope (Code).
ErrNotFound 404 The flight did not operate that day, or no such subscription.
ErrRateLimited 429 A minute, day or month quota is used up. Wait RetryAfter.
ErrUpstream 502 The flight data provider failed. Retry shortly.
ErrServer 5xx Any server error, 502 included. RequestID is what support asks for.
*ConnectionError none No response at all: network error, timeout or a cancelled context. It unwraps to the cause.
_, err := client.Subscriptions.Create(ctx, params)

var apiErr *flightbyte.APIError
switch {
case errors.Is(err, flightbyte.ErrBadRequest) && errors.As(err, &apiErr) && len(apiErr.Legs) > 0:
	for _, leg := range apiErr.Legs {
		fmt.Println("Pick a leg:", leg.Origin, "→", leg.Destination)
	}
case errors.Is(err, flightbyte.ErrPaymentRequired) && errors.As(err, &apiErr):
	fmt.Println("No watched flights left until", apiErr.ResetsAt)
case errors.Is(err, flightbyte.ErrRateLimited) && errors.As(err, &apiErr):
	time.Sleep(apiErr.RetryAfter)
case err != nil:
	return err
}

Every status is explained in the errors docs.

Built for Go services

  • One client, many goroutines. A *Client is safe for concurrent use; create it once and share it. Every call honours its context's deadline and cancellation.
  • No dependencies. Only the standard library: net/http, encoding/json and crypto/hmac. Nothing to audit but this module.
  • Your key stays on your server. An API key or a signing secret shipped inside a mobile or web app can be pulled out of it by anyone who has the app. Flag images are the exception: Countries.FlagURL needs no key.
  • Your key stays with the API. The client follows a redirect only for a read, and only on the API's own host and scheme, so a misdirected answer cannot carry the key elsewhere and a write is never replayed as a GET.
  • New API values don't break old binaries. Phases, statuses, change types and event types are string types, so a value added after this release arrives as is. Known() tells you whether this version has a constant for it.
  • Zero stays apart from unknown. Delays, durations and times the API may leave out are pointers, so a delay of 0 and no delay reading are different things.
  • Errors say what to do next. A number that flies twice comes back with its legs, a 429 with how long to wait, a 402 with the reason and when the allowance resets.
  • Quotas are visible. client.LastRateLimit() returns the minute, day and month windows from the latest response's X-RateLimit-* headers.

Configuration

client, err := flightbyte.NewClient(apiKey,
	flightbyte.WithBaseURL("http://localhost:3000/v1"),
	flightbyte.WithHTTPClient(myHTTPClient),
	flightbyte.WithTimeout(10*time.Second),
	flightbyte.WithUserAgent("my-app/1.2"),
)
Option Default Meaning
apiKey required Your API key, sent as X-API-Key. Keep it on your server.
WithBaseURL https://api.flightbyte.dev/v1 API base URL, e.g. http://localhost:3000/v1 for a local API.
WithHTTPClient a new http.Client Bring your own for retries, proxies, logging or tests. It is copied, not changed. A redirect is followed only for a GET and only on the same host and scheme, so your key stays with the API and a write is never replayed as a GET.
WithTimeout 30 seconds Bounds each request, response body included. 0 leaves it to the context.
WithUserAgent none Prepended to flightbyte-go/<version> in the User-Agent header.

Development

go test -race ./...                # everything, against a local test server
go vet ./...
gofmt -l .                         # prints nothing when formatted
go run honnef.co/go/tools/cmd/staticcheck@latest ./...
go run golang.org/x/vuln/cmd/govulncheck@latest ./...

The tests never touch the real API and need no key. They cover every method's request mapping, the error mapping for each status, the models against fixtures shaped like the API's JSON (decoded strictly, so a field the models lack fails the build), and webhook verification against signatures made with Node's createHmac exactly as the API signs them. CI runs the same commands on Go 1.23 and the latest release.

How to add a field, an enum value or an endpoint is in CONTRIBUTING.md. Report security issues by email, as SECURITY.md explains, never in a public issue.

Project layout
client.go                  NewClient and its options
request.go                 requests, headers, timeouts, error mapping
flights.go                 Flights.Get and Timeline
subscriptions.go           Subscriptions, including the All iterator
countries.go               Countries and flag URLs
flight.go subscription.go  the models
event.go country.go
enums.go                   phases, statuses, change and event types
errors.go                  APIError, the sentinels and ConnectionError
webhook.go                 VerifyWebhook, ConstructWebhookEvent, SignWebhook, WebhookHandler
ratelimit.go               X-RateLimit-* headers
examples/lookup            looks a flight up from the terminal
examples/webhook-server    a receiver on net/http
testdata/                  API-shaped JSON and signed webhook vectors
tool/webhook_vectors.mjs   regenerates the signed vectors with Node, as the API signs
tool/release_notes.sh      a version's CHANGELOG.md section, for the GitHub release
Releasing
  1. Set Version in version.go to the new version.
  2. Give the version its own ## <version> section in CHANGELOG.md.
  3. Push a tag such as v1.0.1.

A Go module is released by its tag; there is nothing to upload. The release workflow checks that the tag matches Version and that the changelog has the section, runs the tests, and creates the GitHub release from the changelog section. Run it by hand from the Actions tab to rehearse without releasing.

License

MIT


FlightByte
FlightByte · Flight intelligence, byte by byte · flightbyte.dev

Documentation

Overview

Package flightbyte is the official Go client for the FlightByte flight status API (https://flightbyte.dev).

Look flights up, watch them with signed webhooks, and verify every delivery:

client, err := flightbyte.NewClient(os.Getenv("FLIGHTBYTE_API_KEY"))
if err != nil {
	log.Fatal(err)
}
flight, err := client.Flights.Get(ctx, flightbyte.FlightParams{
	Carrier:      "TK",
	FlightNumber: "1848",
	Date:         "2026-09-08", // local departure date at the origin
})

Every method takes a context.Context and returns an error. Errors from the API are *APIError values that match sentinels such as ErrNotFound and ErrRateLimited with errors.Is; network failures are *ConnectionError.

Webhooks are verified with VerifyWebhook or ConstructWebhookEvent, or received with WebhookHandler, which does both.

A Client is safe for concurrent use by multiple goroutines.

Index

Examples

Constants

View Source
const (
	// SignatureHeader carries the signature, "t=<unix seconds>,v1=<hex>".
	SignatureHeader = "X-Flightbyte-Signature"
	// TimestampHeader carries the signing time, in unix seconds.
	TimestampHeader = "X-Flightbyte-Timestamp"
	// EventIDHeader carries the event id.
	EventIDHeader = "X-Flightbyte-Event-Id"
	// EventTypeHeader carries the event type.
	EventTypeHeader = "X-Flightbyte-Event-Type"
	// SubscriptionIDHeader carries the subscription id.
	SubscriptionIDHeader = "X-Flightbyte-Subscription-Id"
	// DeliveryAttemptHeader carries the delivery attempt number, starting
	// at 1.
	DeliveryAttemptHeader = "X-Flightbyte-Delivery-Attempt"
)

Headers FlightByte sends with every webhook delivery.

View Source
const DefaultBaseURL = "https://api.flightbyte.dev/v1"

DefaultBaseURL is the production API root.

View Source
const DefaultTimeout = 30 * time.Second

DefaultTimeout bounds each request, response body included, unless WithTimeout says otherwise.

View Source
const DefaultWebhookTolerance = 5 * time.Minute

DefaultWebhookTolerance is how far the signing time may be from now: 5 minutes, as on the API.

View Source
const MaxWebhookBodyBytes = 1 << 20

MaxWebhookBodyBytes is the largest body WebhookHandler reads.

View Source
const Version = "1.0.0"

Version is the version of this package. It is sent in the User-Agent header of every request.

Variables

View Source
var (
	// ErrBadRequest matches a 400: the request was invalid. When a flight
	// number operates several legs that day, [APIError.Legs] lists them.
	ErrBadRequest = errors.New("flightbyte: bad request")
	// ErrUnauthorized matches a 401: the API key is missing, invalid or
	// revoked.
	ErrUnauthorized = errors.New("flightbyte: unauthorized")
	// ErrPaymentRequired matches a 402: no watched flights can be created.
	// [APIError.Code] is "watched_flight_limit" (the plan's cap),
	// "overage_cap_reached" (the account's overage cap) or
	// "credits_exhausted" (prepaid credits used up).
	ErrPaymentRequired = errors.New("flightbyte: payment required")
	// ErrForbidden matches a 403: the account is suspended
	// ("account_suspended") or a token lacks the scope ("insufficient_scope").
	ErrForbidden = errors.New("flightbyte: forbidden")
	// ErrNotFound matches a 404: the flight did not operate, or the
	// subscription does not exist or belongs to another key.
	ErrNotFound = errors.New("flightbyte: not found")
	// ErrRateLimited matches a 429: a minute, day or month quota is used up.
	// [APIError.RetryAfter] says how long to wait.
	ErrRateLimited = errors.New("flightbyte: rate limited")
	// ErrUpstream matches a 502: the upstream flight data provider failed.
	// Safe to retry after a short wait.
	ErrUpstream = errors.New("flightbyte: upstream unavailable")
	// ErrServer matches any 5xx, 502 included.
	ErrServer = errors.New("flightbyte: server error")
)

Sentinels an *APIError matches with errors.Is, by HTTP status.

View Source
var ErrEmptySecret = errors.New("flightbyte: the webhook signing secret is empty")

ErrEmptySecret is returned by VerifyWebhook and ConstructWebhookEvent when the signing secret is empty. Anyone can sign with an empty key, so such a secret would accept forged events. It usually means the secret was not configured, for example an unset environment variable.

View Source
var ErrInvalidSignature = errors.New("flightbyte: invalid webhook signature")

ErrInvalidSignature matches every *WebhookSignatureError with errors.Is.

FlightChangeTypes lists every change type this package knows, in the API's order.

Functions

func FormatDate

func FormatDate(t time.Time) string

FormatDate formats t as the YYYY-MM-DD the API expects for flight dates.

Only the calendar fields are used, in t's own location, so pass the local departure date at the origin airport.

func MaskSecret

func MaskSecret(secret string) string

MaskSecret masks a signing secret the way the API does: the first 11 characters, "...", and the last 4. Short secrets are returned as is.

func Ptr

func Ptr[T any](v T) *T

Ptr returns a pointer to v. It fills optional fields such as CreateSubscriptionParams.DelayThresholdMinutes:

DelayThresholdMinutes: flightbyte.Ptr(15)

func SignWebhook

func SignWebhook(payload []byte, secret string, at time.Time) string

SignWebhook signs payload the way the API does and returns the X-Flightbyte-Signature header value. Useful for testing your receiver.

func VerifyWebhook

func VerifyWebhook(payload []byte, signature, secret string, opts ...VerifyOption) error

VerifyWebhook checks signature (the X-Flightbyte-Signature header) against the raw request body payload and the subscription's signing secret.

The signature has the form "t=<unix seconds>,v1=<hex>", where the hex is HMAC-SHA256 of "<t>.<payload>" keyed with the secret. payload must be the body exactly as received, before any JSON decoding. A signing time further than the tolerance from now is rejected, which stops replays.

It returns a *WebhookSignatureError, matching ErrInvalidSignature, when the signature does not verify, and ErrEmptySecret when secret is empty.

Example
package main

import (
	"errors"
	"fmt"
	"time"

	flightbyte "github.com/FlightByte-dev/flightbyte-go"
)

func main() {
	secret := "whsec_example"
	payload := []byte(`{"id":"evt_1","type":"subscription.test"}`)
	signature := flightbyte.SignWebhook(payload, secret, time.Now())

	err := flightbyte.VerifyWebhook(payload, signature, secret)
	fmt.Println("valid:", err == nil)

	err = flightbyte.VerifyWebhook(payload, signature, "the wrong secret")
	fmt.Println("rejected:", errors.Is(err, flightbyte.ErrInvalidSignature))
}
Output:
valid: true
rejected: true

func WebhookHandler

func WebhookHandler(secret string, handle func(context.Context, *WebhookEvent) error, opts ...VerifyOption) http.Handler

WebhookHandler returns an http.Handler that receives FlightByte webhooks. It reads up to MaxWebhookBodyBytes of the body, verifies the signature with secret, decodes the event and calls handle.

It answers 401 when the signature does not verify, 400 when the body is not an event, 500 when handle returns an error (so the API retries the delivery) and 200 otherwise. Retries carry the same event id, so make handle idempotent by deduplicating on WebhookEvent.ID.

An error from handle is logged with the log package, unless WithErrorHandler says otherwise.

The API retries a delivery that takes too long, so do slow work after returning, for example on a queue.

WebhookHandler panics when secret is empty, which usually means it was not configured: anyone can sign with an empty key. It panics when handle is nil too.

Example
package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"os"

	flightbyte "github.com/FlightByte-dev/flightbyte-go"
)

func main() {
	secret := os.Getenv("FLIGHTBYTE_WEBHOOK_SECRET")
	http.Handle("POST /hooks/flightbyte", flightbyte.WebhookHandler(secret,
		func(ctx context.Context, event *flightbyte.WebhookEvent) error {
			// Retries carry the same event.ID: deduplicate on it.
			for _, change := range event.Changes {
				fmt.Println(event.FlightKey, change.Message)
			}
			return nil
		}))
	log.Fatal(http.ListenAndServe(":8080", nil))
}

Types

type APIError

type APIError struct {
	// StatusCode is the HTTP status.
	StatusCode int
	// Messages holds every message the API sent. Validation errors send one
	// per field.
	Messages []string
	// Code is a machine-readable reason, when the API sends one, for example
	// "watched_flight_limit" or "account_suspended".
	Code string
	// RequestID identifies the request; quote it when contacting support.
	RequestID string

	// Legs lists the legs to choose from, on a 400 for a flight number that
	// operates several legs that day. Pass one's Origin or FlightID and try
	// again.
	Legs []AmbiguousLeg
	// Limit is the number of watched flights the period allows, on a 402.
	// Not with "credits_exhausted".
	Limit *int
	// Used is the number of watched flights created this period, on a 402.
	// Not with "credits_exhausted".
	Used *int
	// ResetsAt is when the period, and so the count, resets, on a 402.
	// Not with "credits_exhausted".
	ResetsAt *time.Time
	// CapCents is the account's overage cap in US cents, on a 402 with
	// "overage_cap_reached".
	CapCents *int
	// Balance is the prepaid credits left, on a 402 with "credits_exhausted".
	Balance *int
	// ExpiresAt is when the prepaid credits expire or expired, 12 months
	// after the last top-up, on a 402 with "credits_exhausted". Nil when the
	// API sends none.
	ExpiresAt *time.Time
	// Window is the quota window that ran out, on a 429: "minute", "day" or
	// "month".
	Window string
	// RetryAfter is how long to wait before retrying, on a 429.
	RetryAfter time.Duration

	// Body is the decoded error body; empty when it was not a JSON object.
	Body map[string]any
	// RawBody is the body exactly as received.
	RawBody []byte
	// Header holds the response headers.
	Header http.Header
}

APIError is returned when the API answers with a non-2xx status. Its fields mirror the API's error body.

Branch on it with errors.Is and the sentinels, or read its fields with errors.As:

var apiErr *flightbyte.APIError
if errors.As(err, &apiErr) && errors.Is(err, flightbyte.ErrRateLimited) {
	time.Sleep(apiErr.RetryAfter)
}
Example
package main

import (
	"context"
	"errors"
	"fmt"
	"log"
	"os"

	flightbyte "github.com/FlightByte-dev/flightbyte-go"
)

func main() {
	client, err := flightbyte.NewClient(os.Getenv("FLIGHTBYTE_API_KEY"))
	if err != nil {
		log.Fatal(err)
	}
	_, err = client.Flights.Get(context.Background(), flightbyte.FlightParams{
		Carrier: "TK", FlightNumber: "1", Date: "2026-09-08",
	})

	var apiErr *flightbyte.APIError
	switch {
	case errors.Is(err, flightbyte.ErrNotFound):
		fmt.Println("the flight did not operate that day")
	case errors.Is(err, flightbyte.ErrBadRequest) && errors.As(err, &apiErr) && len(apiErr.Legs) > 0:
		for _, leg := range apiErr.Legs {
			fmt.Println("choose a leg:", leg.Origin, "→", leg.Destination)
		}
	case errors.Is(err, flightbyte.ErrRateLimited) && errors.As(err, &apiErr):
		fmt.Println("retry in", apiErr.RetryAfter)
	case err != nil:
		log.Fatal(err)
	}
}

func (*APIError) Error

func (e *APIError) Error() string

Error describes the status, the API's messages and the request id.

func (*APIError) Is

func (e *APIError) Is(target error) bool

Is reports whether target is the sentinel for e's status.

type Airport

type Airport struct {
	// IATA is the IATA code, for example "IST".
	IATA string `json:"iata"`
	// ICAO is the ICAO code, for example "LTFM".
	ICAO string `json:"icao"`
	// Code is the upstream's own airport code; it may differ from IATA.
	Code string `json:"code"`
	// Name is the airport name.
	Name string `json:"name"`
	// City is the city.
	City string `json:"city"`
	// State is the state or region.
	State string `json:"state"`
	// Country is the country.
	Country CountryRef `json:"country"`
	// Timezone is the IANA time zone, for example "Europe/Istanbul".
	Timezone string `json:"timezone"`
}

Airport is an airport.

type AmbiguousLeg

type AmbiguousLeg struct {
	// Origin is the origin IATA code.
	Origin string `json:"origin"`
	// Destination is the destination IATA code.
	Destination string `json:"destination"`
	// Carrier is the operating carrier.
	Carrier string `json:"carrier"`
	// ScheduledDepartureLocal is the scheduled local departure, "HH:mm".
	ScheduledDepartureLocal string `json:"scheduledDepartureLocal"`
	// FlightID is the upstream flight id.
	FlightID string `json:"flightId"`
}

AmbiguousLeg is one of the legs listed by APIError.Legs.

type Carrier

type Carrier struct {
	// IATA is the IATA code, for example "TK".
	IATA string `json:"iata"`
	// ICAO is the ICAO code, for example "THY".
	ICAO string `json:"icao"`
	// Name is the airline name.
	Name string `json:"name"`
}

Carrier is the marketing carrier.

type Client

type Client struct {
	// Flights looks flights up.
	Flights *FlightsService
	// Subscriptions watches flights and manages their webhooks.
	Subscriptions *SubscriptionsService
	// Countries lists countries and their flags.
	Countries *CountriesService
	// contains filtered or unexported fields
}

Client talks to the FlightByte API. Create one with NewClient and share it: it is safe for concurrent use by multiple goroutines.

Keep API keys on a server you control. A key shipped inside a mobile or web app can be extracted by anyone who has the app.

func NewClient

func NewClient(apiKey string, opts ...Option) (*Client, error)

NewClient returns a client that authenticates with apiKey.

Example
package main

import (
	"log"
	"os"
	"time"

	flightbyte "github.com/FlightByte-dev/flightbyte-go"
)

func main() {
	client, err := flightbyte.NewClient(os.Getenv("FLIGHTBYTE_API_KEY"),
		flightbyte.WithTimeout(10*time.Second),
		flightbyte.WithUserAgent("my-app/1.0"),
	)
	if err != nil {
		log.Fatal(err)
	}
	_ = client
}

func (*Client) BaseURL

func (c *Client) BaseURL() string

BaseURL returns the API root this client talks to.

func (*Client) LastRateLimit

func (c *Client) LastRateLimit() *RateLimit

LastRateLimit returns the quota state from the latest response that reported one, or nil before any did. Keys without limits get none.

type Codeshare

type Codeshare struct {
	// Carrier is the carrier code.
	Carrier string `json:"carrier"`
	// Name is the carrier name.
	Name string `json:"name"`
	// FlightNumber is the flight number.
	FlightNumber string `json:"flightNumber"`
}

Codeshare is a codeshare flight number sold on the same flight.

type ConnectionError

type ConnectionError struct {
	// Method is the HTTP method.
	Method string
	// URL is the request URL.
	URL string
	// Err is the cause.
	Err error
}

ConnectionError is returned when a request got no HTTP response: no network, a DNS failure, a refused connection, a timeout or a cancelled context. It unwraps to the cause, so errors.Is(err, context.DeadlineExceeded) works.

func (*ConnectionError) Error

func (e *ConnectionError) Error() string

Error describes the failed request and its cause.

func (*ConnectionError) Unwrap

func (e *ConnectionError) Unwrap() error

Unwrap returns the cause.

type CountriesService

type CountriesService struct {
	// contains filtered or unexported fields
}

CountriesService lists country names, emoji and flags. Reach it as client.Countries.

func (*CountriesService) FlagURL

func (s *CountriesService) FlagURL(code string, square bool) string

FlagURL returns the URL of a country's SVG flag: 4:3, or 1:1 when square. Flags need no API key, so the URL can go straight into an img tag.

func (*CountriesService) Get

func (s *CountriesService) Get(ctx context.Context, code, lang string) (*Country, error)

Get returns one country by its ISO 3166-1 alpha-2 code.

func (*CountriesService) List

func (s *CountriesService) List(ctx context.Context, lang string) ([]Country, error)

List returns every country, sorted by name, with names in lang (for example "tr"). An empty lang uses the API's default.

type Country

type Country struct {
	CountryRef
	// Alpha2 is the ISO 3166-1 alpha-2 code.
	Alpha2 string `json:"alpha2"`
	// HasFlag reports whether a flag image exists for the country.
	HasFlag bool `json:"hasFlag"`
}

Country is a country from the /countries endpoints.

type CountryRef

type CountryRef struct {
	// Code is the ISO 3166-1 alpha-2 code, for example "TR".
	Code string `json:"code"`
	// Name is the country name.
	Name string `json:"name"`
	// Emoji is the flag emoji.
	Emoji string `json:"emoji"`
	// FlagSVG is the absolute URL of the 4:3 SVG flag.
	FlagSVG string `json:"flagSvg"`
	// FlagSVGSquare is the absolute URL of the 1:1 SVG flag.
	FlagSVGSquare string `json:"flagSvgSquare"`
}

CountryRef is a country as it appears inside an airport.

type CreateSubscriptionParams

type CreateSubscriptionParams struct {
	// Carrier is the carrier code, for example "TK". Required.
	Carrier string `json:"carrier"`
	// FlightNumber is the flight number, for example "1848". Required.
	FlightNumber string `json:"flightNumber"`
	// Date is the local departure date at the origin, YYYY-MM-DD. Required.
	Date string `json:"date"`
	// WebhookURL is where webhooks are posted. Required.
	WebhookURL string `json:"webhookUrl"`
	// Origin selects a leg, when the flight number operates several that day.
	Origin string `json:"origin,omitempty"`
	// Destination narrows the leg further.
	Destination string `json:"destination,omitempty"`
	// FlightID selects a leg by its upstream id.
	FlightID string `json:"flightId,omitempty"`
	// Secret sets the webhook signing secret. By default the API generates
	// one, returned once in [CreatedSubscription.Secret]. Setting the same
	// secret on every subscription lets one [WebhookHandler] verify them all.
	Secret string `json:"secret,omitempty"`
	// Events filters the change types. By default every type is sent.
	Events []FlightChangeType `json:"events,omitempty"`
	// DelayThresholdMinutes (1 to 240) ignores smaller delay swings.
	DelayThresholdMinutes *int `json:"delayThresholdMinutes,omitempty"`
	// Headers are added to every delivery.
	Headers map[string]string `json:"headers,omitempty"`
	// Metadata is echoed back in every webhook.
	Metadata map[string]string `json:"metadata,omitempty"`
	// IdempotencyKey makes retries safe: repeating the request with the same
	// key within 24 hours returns the original subscription with Reused set,
	// instead of creating and billing a second one.
	IdempotencyKey string `json:"-"`
}

CreateSubscriptionParams describes a flight to watch.

type CreatedSubscription

type CreatedSubscription struct {
	Subscription
	// Secret is the full webhook signing secret. The API returns it in full
	// only here and from [SubscriptionsService.Secret]; store it where your
	// webhook receiver can read it.
	Secret string `json:"secret"`
	// Reused is true when an earlier request with the same idempotency key,
	// within 24 hours, was replayed: the original subscription is returned and
	// nothing new is created or billed.
	Reused bool `json:"reused"`
	// Flight is the flight as first seen, when it was found. Nil when Reused.
	Flight *Flight `json:"flight"`
}

CreatedSubscription is the response of SubscriptionsService.Create.

type Delivery

type Delivery struct {
	// EventID is the delivered event's id.
	EventID string `json:"eventId"`
	// Attempt is the attempt number, starting at 1.
	Attempt int `json:"attempt"`
	// Status is the outcome.
	Status DeliveryStatus `json:"status"`
	// HTTPStatus is the receiver's HTTP status, when it answered.
	HTTPStatus *int `json:"httpStatus"`
	// Error says what went wrong, when the attempt failed.
	Error string `json:"error"`
	// DurationMs is how long the attempt took, in milliseconds.
	DurationMs int `json:"durationMs"`
	// At is when the attempt was made.
	At time.Time `json:"at"`
	// ChangeTypes lists the change types the event carried.
	ChangeTypes []FlightChangeType `json:"changeTypes"`
}

Delivery is one webhook delivery attempt.

type DeliveryStatus

type DeliveryStatus string

DeliveryStatus is the outcome of one webhook delivery attempt.

const (
	// DeliveryDelivered: the receiver answered 2xx.
	DeliveryDelivered DeliveryStatus = "delivered"
	// DeliveryFailed: the attempt failed.
	DeliveryFailed DeliveryStatus = "failed"
)

Delivery statuses.

func (DeliveryStatus) Known

func (s DeliveryStatus) Known() bool

Known reports whether s is one of the constants above.

type Endpoint

type Endpoint struct {
	// Airport is the airport.
	Airport Airport `json:"airport"`
	// Terminal is the terminal.
	Terminal string `json:"terminal"`
	// Gate is the gate.
	Gate string `json:"gate"`
	// Baggage is the baggage carousel, on arrival.
	Baggage string `json:"baggage"`
	// Times holds the gate and runway times.
	Times EndpointTimes `json:"times"`
	// BestTimeUTC is the best known time: actual, else estimated, else
	// scheduled.
	BestTimeUTC *time.Time `json:"bestTimeUtc"`
	// DelayMinutes is the gate delay in minutes; negative when early.
	DelayMinutes *int `json:"delayMinutes"`
}

Endpoint is the departure or arrival side of a flight.

type EndpointTimes

type EndpointTimes struct {
	// ScheduledGate is the scheduled gate time.
	ScheduledGate *FlightTime `json:"scheduledGate"`
	// EstimatedGate is the estimated gate time.
	EstimatedGate *FlightTime `json:"estimatedGate"`
	// ActualGate is the actual gate time.
	ActualGate *FlightTime `json:"actualGate"`
	// ScheduledRunway is the scheduled runway time.
	ScheduledRunway *FlightTime `json:"scheduledRunway"`
	// EstimatedRunway is the estimated runway time.
	EstimatedRunway *FlightTime `json:"estimatedRunway"`
	// ActualRunway is the actual runway time.
	ActualRunway *FlightTime `json:"actualRunway"`
}

EndpointTimes holds the gate and runway times of a departure or an arrival.

func (EndpointTimes) BestGate

func (t EndpointTimes) BestGate() *FlightTime

BestGate returns the best known gate time: actual, else estimated, else scheduled. It returns nil when none is known.

type Equipment

type Equipment struct {
	// TailNumber is the registration, for example "TC-LTP".
	TailNumber string `json:"tailNumber"`
	// IATA is the IATA aircraft type, for example "32Q".
	IATA string `json:"iata"`
	// Name is the aircraft name, for example "Airbus A321neo".
	Name string `json:"name"`
	// Source is where the data came from, for example "Actual".
	Source string `json:"source"`
}

Equipment is the aircraft.

type Flight

type Flight struct {
	// FlightKey is a stable key, for example "TK1848:2026-09-08" or
	// "TK1:2026-09-08@IST-JFK".
	FlightKey string `json:"flightKey"`
	// CarrierCode is the carrier code from the request, for example "TK".
	CarrierCode string `json:"carrierCode"`
	// FlightNumber is the flight number without leading zeros.
	FlightNumber string `json:"flightNumber"`
	// Date is the local departure date at the origin, YYYY-MM-DD.
	Date string `json:"date"`
	// ProviderFlightID is the upstream flight id.
	ProviderFlightID string `json:"providerFlightId"`
	// FetchedAt is when the API fetched this data from upstream.
	FetchedAt time.Time `json:"fetchedAt"`
	// Carrier is the marketing carrier.
	Carrier Carrier `json:"carrier"`
	// OperatedBy is the operating carrier's name, when different.
	OperatedBy string `json:"operatedBy"`
	// Codeshares lists the codeshare flight numbers.
	Codeshares []Codeshare `json:"codeshares"`
	// Departure is the departure side.
	Departure Endpoint `json:"departure"`
	// Arrival is the arrival side.
	Arrival Endpoint `json:"arrival"`
	// Diverted is where the flight actually went, when diverted.
	Diverted *Endpoint `json:"diverted"`
	// Status is the flight's status.
	Status FlightStatus `json:"status"`
	// Equipment is the aircraft.
	Equipment Equipment `json:"equipment"`
	// Duration holds the block times.
	Duration FlightDuration `json:"duration"`
	// Timeline is the upstream history, newest first.
	Timeline []TimelineEntry `json:"timeline"`
	// Alternates lists every leg flown under this flight number on this date.
	Alternates []FlightLeg `json:"alternates"`
	// Fingerprint is a hash over the change-relevant fields.
	Fingerprint string `json:"fingerprint"`
	// Cached reports whether a lookup was answered from the API's cache.
	// Always false on flights inside webhook events.
	Cached bool `json:"cached"`
}

Flight is everything known about one flight leg at one moment.

Strings the API may send as null are empty here; numbers and times it may send as null are pointers, so zero and unknown stay apart.

type FlightChange

type FlightChange struct {
	// Type says what changed.
	Type FlightChangeType `json:"type"`
	// Path is the dotted path into the flight, for example "departure.gate".
	Path string `json:"path"`
	// From is the old value: a string, a float64 or nil. Times are
	// RFC 3339 UTC strings.
	From any `json:"from"`
	// To is the new value: a string, a float64 or nil. Times are RFC 3339
	// UTC strings.
	To any `json:"to"`
	// Message is a sentence describing the change.
	Message string `json:"message"`
	// DeltaMinutes is the signed minute delta, for time changes.
	DeltaMinutes *int `json:"deltaMinutes"`
}

FlightChange is one change inside a flight.updated event.

type FlightChangeType

type FlightChangeType string

FlightChangeType is the kind of change a flight.updated webhook reports, and what a subscription's Events filter is made of.

const (
	ChangeStatus             FlightChangeType = "status.changed"
	ChangeDeparted           FlightChangeType = "flight.departed"
	ChangeArrived            FlightChangeType = "flight.arrived"
	ChangeCancelled          FlightChangeType = "flight.cancelled"
	ChangeDiverted           FlightChangeType = "flight.diverted"
	ChangeReinstated         FlightChangeType = "flight.reinstated"
	ChangeDepartureGate      FlightChangeType = "departure.gate.changed"
	ChangeDepartureTerminal  FlightChangeType = "departure.terminal.changed"
	ChangeArrivalGate        FlightChangeType = "arrival.gate.changed"
	ChangeArrivalTerminal    FlightChangeType = "arrival.terminal.changed"
	ChangeBaggage            FlightChangeType = "arrival.baggage.changed"
	ChangeDepartureScheduled FlightChangeType = "departure.scheduled.changed"
	ChangeDepartureEstimated FlightChangeType = "departure.estimated.changed"
	ChangeDepartureActual    FlightChangeType = "departure.actual.changed"
	ChangeArrivalScheduled   FlightChangeType = "arrival.scheduled.changed"
	ChangeArrivalEstimated   FlightChangeType = "arrival.estimated.changed"
	ChangeArrivalActual      FlightChangeType = "arrival.actual.changed"
	ChangeDelayIncreased     FlightChangeType = "delay.increased"
	ChangeDelayDecreased     FlightChangeType = "delay.decreased"
	ChangeAircraft           FlightChangeType = "aircraft.changed"
	ChangeTailNumber         FlightChangeType = "aircraft.tail.changed"
	ChangeRoute              FlightChangeType = "route.changed"
	ChangeCodeshares         FlightChangeType = "codeshares.changed"
)

Flight change types.

func (FlightChangeType) Known

func (t FlightChangeType) Known() bool

Known reports whether t is one of the constants above.

type FlightDuration

type FlightDuration struct {
	// ScheduledMinutes is the scheduled duration.
	ScheduledMinutes *int `json:"scheduledMinutes"`
	// EstimatedMinutes is the estimated duration.
	EstimatedMinutes *int `json:"estimatedMinutes"`
	// ActualMinutes is the actual duration.
	ActualMinutes *int `json:"actualMinutes"`
}

FlightDuration holds block times in minutes.

type FlightLeg

type FlightLeg struct {
	// ProviderFlightID is the upstream flight id; pass it as
	// [FlightParams.FlightID] to select this leg.
	ProviderFlightID string `json:"providerFlightId"`
	// Carrier is the operating carrier code.
	Carrier string `json:"carrier"`
	// DepartureIATA is the origin IATA code.
	DepartureIATA string `json:"departureIata"`
	// ArrivalIATA is the destination IATA code.
	ArrivalIATA string `json:"arrivalIata"`
	// ScheduledDepartureLocal is the scheduled local departure, "HH:mm".
	ScheduledDepartureLocal string `json:"scheduledDepartureLocal"`
	// ScheduledArrivalLocal is the scheduled local arrival, "HH:mm".
	ScheduledArrivalLocal string `json:"scheduledArrivalLocal"`
	// Selected reports whether this is the leg the flight describes.
	Selected bool `json:"selected"`
}

FlightLeg is a leg listed for the same flight number on the same date.

Flight numbers are not unique per day, so the API lists every leg and marks the one the flight describes with Selected.

type FlightParams

type FlightParams struct {
	// Carrier is the IATA or ICAO carrier code, for example "TK". Required.
	Carrier string
	// FlightNumber is the flight number, for example "1848". Required.
	FlightNumber string
	// Date is the local departure date at the origin, YYYY-MM-DD; see
	// [FormatDate]. Required.
	Date string
	// Origin selects a leg by its origin IATA code, when the flight number
	// operates several legs that day.
	Origin string
	// Destination narrows the leg further by its destination IATA code.
	Destination string
	// FlightID selects a leg by its upstream id, from [FlightLeg]. Ignored by
	// Timeline.
	FlightID string
	// Refresh skips the API's cache and asks upstream. Ignored by Timeline.
	Refresh bool
}

FlightParams identifies a flight.

type FlightPhase

type FlightPhase string

FlightPhase is the coarse lifecycle bucket of a flight.

const (
	PhaseScheduled  FlightPhase = "scheduled"
	PhaseBoarding   FlightPhase = "boarding"
	PhaseDeparted   FlightPhase = "departed"
	PhaseEnRoute    FlightPhase = "en_route"
	PhaseLanded     FlightPhase = "landed"
	PhaseArrived    FlightPhase = "arrived"
	PhaseCancelled  FlightPhase = "cancelled"
	PhaseDiverted   FlightPhase = "diverted"
	PhaseRedirected FlightPhase = "redirected"
	PhaseUnknown    FlightPhase = "unknown"
)

Flight phases.

func (FlightPhase) IsTerminal

func (p FlightPhase) IsTerminal() bool

IsTerminal reports whether the flight is finished: arrived, cancelled, diverted or redirected.

func (FlightPhase) Known

func (p FlightPhase) Known() bool

Known reports whether p is one of the constants above.

type FlightStatus

type FlightStatus struct {
	// Code is the upstream status code: S, A, L, C, D, R, U or NO.
	Code string `json:"code"`
	// Label is the human-readable status, for example "Arrived".
	Label string `json:"label"`
	// Description is a longer description, for example "On time".
	Description string `json:"description"`
	// Color is a suggested color, for example "green".
	Color string `json:"color"`
	// Phase is the lifecycle phase.
	Phase FlightPhase `json:"phase"`
	// Delayed reports whether the flight is delayed.
	Delayed bool `json:"delayed"`
	// Cancelled reports whether the flight is cancelled.
	Cancelled bool `json:"cancelled"`
	// Diverted reports whether the flight is diverted.
	Diverted bool `json:"diverted"`
	// DepartureGateDelayMinutes is the departure gate delay in minutes.
	DepartureGateDelayMinutes *int `json:"departureGateDelayMinutes"`
	// ArrivalGateDelayMinutes is the arrival gate delay in minutes.
	ArrivalGateDelayMinutes *int `json:"arrivalGateDelayMinutes"`
}

FlightStatus is the status of a flight.

type FlightTime

type FlightTime struct {
	// UTC is the instant.
	UTC time.Time `json:"utc"`
	// Local is the airport-local wall time without an offset, for example
	// "2026-09-08T12:10:00.000". It is a string because it carries no offset;
	// pair it with the airport's time zone.
	Local string `json:"local"`
	// Timezone is the time zone abbreviation, for example "EEST".
	Timezone string `json:"timezone"`
}

FlightTime is a point in time, in UTC and in the airport's local time.

type FlightTimeline

type FlightTimeline struct {
	// FlightKey is the flight's key.
	FlightKey string `json:"flightKey"`
	// Count is the number of entries.
	Count int `json:"count"`
	// Entries holds the history entries, newest first.
	Entries []TimelineEntry `json:"data"`
}

FlightTimeline is the response of FlightsService.Timeline.

type FlightsService

type FlightsService struct {
	// contains filtered or unexported fields
}

FlightsService looks flights up. Reach it as client.Flights.

func (*FlightsService) Get

func (s *FlightsService) Get(ctx context.Context, params FlightParams) (*Flight, error)

Get looks a flight up. The API normalizes the carrier to upper case and strips leading zeros from the flight number.

A flight number can operate several legs on one date. The flight lists them all in Flight.Alternates; set Origin (and optionally Destination) or a leg's FlightID to choose one. Without them, an ambiguous flight returns an *APIError matching ErrBadRequest whose Legs lists the choices.

It returns an error matching ErrNotFound when the flight did not operate that day.

Example
package main

import (
	"context"
	"fmt"
	"log"
	"os"

	flightbyte "github.com/FlightByte-dev/flightbyte-go"
)

func main() {
	client, err := flightbyte.NewClient(os.Getenv("FLIGHTBYTE_API_KEY"))
	if err != nil {
		log.Fatal(err)
	}
	flight, err := client.Flights.Get(context.Background(), flightbyte.FlightParams{
		Carrier:      "TK",
		FlightNumber: "1848",
		Date:         "2026-09-08", // local departure date at the origin
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(flight.Status.Label, flight.Departure.Gate)
}

func (*FlightsService) Timeline

func (s *FlightsService) Timeline(ctx context.Context, params FlightParams) (*FlightTimeline, error)

Timeline returns the flight's change history as recorded by the API, newest first.

type ListSubscriptionsParams

type ListSubscriptionsParams struct {
	// Status keeps subscriptions in this status.
	Status SubscriptionStatus
	// Carrier keeps subscriptions for this carrier.
	Carrier string
	// FlightNumber keeps subscriptions for this flight number.
	FlightNumber string
	// Date keeps subscriptions for this date, YYYY-MM-DD.
	Date string
	// Limit is the page size; the API clamps it to 1 to 200 and defaults
	// to 25.
	Limit int
	// Offset is the number of subscriptions to skip.
	Offset int
}

ListSubscriptionsParams filters SubscriptionsService.List. Zero values are not sent.

type Option

type Option func(*Client) error

Option configures a Client.

func WithBaseURL

func WithBaseURL(baseURL string) Option

WithBaseURL points the client at another API root, for example "http://localhost:3000/v1".

func WithHTTPClient

func WithHTTPClient(httpClient *http.Client) Option

WithHTTPClient sends requests through httpClient, for example one with a custom transport for retries, proxies or logging.

The client is copied, not changed. The copy follows a redirect only for a GET or HEAD and only to the same scheme and host, so the API key never reaches another server and a write is never replayed as a GET; within that, httpClient's own CheckRedirect still applies.

func WithTimeout

func WithTimeout(timeout time.Duration) Option

WithTimeout bounds each request, response body included. Zero disables the limit and leaves it to the context. The default is DefaultTimeout.

func WithUserAgent

func WithUserAgent(userAgent string) Option

WithUserAgent puts userAgent in front of this package's own User-Agent, so requests read as, for example, "my-app/1.2 flightbyte-go/1.0.0".

type RateLimit

type RateLimit struct {
	// Minute is the per-minute window.
	Minute *RateLimitWindow
	// Day is the per-day window.
	Day *RateLimitWindow
	// Month is the per-month window.
	Month *RateLimitWindow
}

RateLimit is the quota state sent with a response, read from the X-RateLimit-{Limit,Remaining,Reset}-{Minute,Day,Month} headers. A window the key has no limit on is nil.

type RateLimitWindow

type RateLimitWindow struct {
	// Limit is the number of requests the window allows.
	Limit int
	// Remaining is the number of requests left in the window.
	Remaining int
	// ResetAt is when the window resets; zero when not reported.
	ResetAt time.Time
}

RateLimitWindow is one quota window.

func (RateLimitWindow) String

func (w RateLimitWindow) String() string

String formats the window as "remaining/limit".

type Subscription

type Subscription struct {
	// ID is the id, for example "sub_7Kd91mQx0Zb2c3d4e5f6".
	ID string `json:"id"`
	// Status is the lifecycle status.
	Status SubscriptionStatus `json:"status"`
	// FlightKey is the watched flight's key, for example "TK1848:2026-09-08".
	FlightKey string `json:"flightKey"`
	// Carrier is the carrier code.
	Carrier string `json:"carrier"`
	// FlightNumber is the flight number.
	FlightNumber string `json:"flightNumber"`
	// Date is the local departure date at the origin, YYYY-MM-DD.
	Date string `json:"date"`
	// WebhookURL is where webhooks are posted.
	WebhookURL string `json:"webhookUrl"`
	// SecretHint is the masked signing secret, for example
	// "whsec_AbCdE...wxyz".
	SecretHint string `json:"secretHint"`
	// Headers are extra headers sent with every delivery.
	Headers map[string]string `json:"headers"`
	// Events lists the change types that trigger a webhook.
	Events []FlightChangeType `json:"events"`
	// DelayThresholdMinutes: delay swings smaller than this are ignored.
	DelayThresholdMinutes int `json:"delayThresholdMinutes"`
	// Metadata is your own data, echoed back in every webhook.
	Metadata map[string]string `json:"metadata"`
	// CreatedAt is when it was created.
	CreatedAt time.Time `json:"createdAt"`
	// UpdatedAt is when it last changed.
	UpdatedAt time.Time `json:"updatedAt"`
	// LastPolledAt is when the flight was last polled.
	LastPolledAt *time.Time `json:"lastPolledAt"`
	// LastChangeAt is when a change was last detected.
	LastChangeAt *time.Time `json:"lastChangeAt"`
	// LastDeliveryAt is when a webhook was last delivered.
	LastDeliveryAt *time.Time `json:"lastDeliveryAt"`
	// DeliveredCount is the number of webhooks delivered.
	DeliveredCount int `json:"deliveredCount"`
	// FailedCount is the number of webhook attempts that failed.
	FailedCount int `json:"failedCount"`
	// FlightResolved is false while the flight is not found upstream yet.
	FlightResolved bool `json:"flightResolved"`
	// StatusReason says why the subscription was suspended or completed.
	StatusReason string `json:"statusReason"`
}

Subscription is a watched flight: the API polls it and posts a signed webhook to WebhookURL whenever something about it changes.

type SubscriptionPage

type SubscriptionPage struct {
	// Total is the number of subscriptions matching the filters.
	Total int `json:"total"`
	// Limit is the page size used.
	Limit int `json:"limit"`
	// Offset is the number of subscriptions skipped.
	Offset int `json:"offset"`
	// Items holds the subscriptions on this page.
	Items []Subscription `json:"data"`
}

SubscriptionPage is one page of SubscriptionsService.List.

func (*SubscriptionPage) HasMore

func (p *SubscriptionPage) HasMore() bool

HasMore reports whether more pages follow.

func (*SubscriptionPage) NextOffset

func (p *SubscriptionPage) NextOffset() int

NextOffset returns the offset of the next page.

type SubscriptionStatus

type SubscriptionStatus string

SubscriptionStatus is the lifecycle of a subscription.

const (
	// StatusActive: polling the flight and delivering webhooks.
	StatusActive SubscriptionStatus = "active"
	// StatusPending: accepted, but the flight is not published upstream yet.
	StatusPending SubscriptionStatus = "pending"
	// StatusPaused: paused by you; nothing is polled or delivered.
	StatusPaused SubscriptionStatus = "paused"
	// StatusCompleted: the flight finished and the grace window passed.
	StatusCompleted SubscriptionStatus = "completed"
	// StatusSuspended: disabled by the API, for example after repeated
	// delivery failures.
	StatusSuspended SubscriptionStatus = "suspended"
)

Subscription statuses.

func (SubscriptionStatus) Known

func (s SubscriptionStatus) Known() bool

Known reports whether s is one of the constants above.

type SubscriptionsService

type SubscriptionsService struct {
	// contains filtered or unexported fields
}

SubscriptionsService watches flights and manages their webhooks. Reach it as client.Subscriptions.

Every subscription is a billable watched flight.

func (*SubscriptionsService) All

All returns every subscription matching params, fetched page by page as the loop asks for more. params.Limit sets the page size (default 200) and params.Offset where to start.

for sub, err := range client.Subscriptions.All(ctx, flightbyte.ListSubscriptionsParams{}) {
	if err != nil {
		return err
	}
	fmt.Println(sub.FlightKey)
}

After an error the sequence ends.

Example
package main

import (
	"context"
	"fmt"
	"log"
	"os"

	flightbyte "github.com/FlightByte-dev/flightbyte-go"
)

func main() {
	client, err := flightbyte.NewClient(os.Getenv("FLIGHTBYTE_API_KEY"))
	if err != nil {
		log.Fatal(err)
	}
	params := flightbyte.ListSubscriptionsParams{Status: flightbyte.StatusActive}
	for sub, err := range client.Subscriptions.All(context.Background(), params) {
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println(sub.FlightKey, sub.Status)
	}
}

func (*SubscriptionsService) Create

Create starts watching a flight. The API posts a signed webhook to WebhookURL each time something about it changes.

It returns an error matching ErrPaymentRequired when the period's watched flights are used up, and one matching ErrBadRequest, with APIError.Legs set, when the flight number operates several legs that day and neither Origin nor FlightID chooses one.

Example
package main

import (
	"context"
	"fmt"
	"log"
	"os"

	flightbyte "github.com/FlightByte-dev/flightbyte-go"
)

func main() {
	client, err := flightbyte.NewClient(os.Getenv("FLIGHTBYTE_API_KEY"))
	if err != nil {
		log.Fatal(err)
	}
	created, err := client.Subscriptions.Create(context.Background(), flightbyte.CreateSubscriptionParams{
		Carrier:               "TK",
		FlightNumber:          "1848",
		Date:                  "2026-09-08",
		WebhookURL:            "https://example.com/hooks/flightbyte",
		Events:                []flightbyte.FlightChangeType{flightbyte.ChangeDepartureGate, flightbyte.ChangeDelayIncreased},
		DelayThresholdMinutes: flightbyte.Ptr(15),
		Metadata:              map[string]string{"booking": "42"},
		IdempotencyKey:        "booking-42", // safe to retry
	})
	if err != nil {
		log.Fatal(err)
	}
	// The full secret comes back only here: store it for your receiver.
	fmt.Println(created.ID, created.SecretHint)
}

func (*SubscriptionsService) Delete

func (s *SubscriptionsService) Delete(ctx context.Context, id string) error

Delete stops watching the flight and deletes the subscription.

func (*SubscriptionsService) Deliveries

func (s *SubscriptionsService) Deliveries(ctx context.Context, id string, limit int) ([]Delivery, error)

Deliveries returns the latest delivery attempts, newest first. limit is 1 to 100; zero means the API's default of 25.

func (*SubscriptionsService) Events

func (s *SubscriptionsService) Events(ctx context.Context, id string, limit int) ([]WebhookEvent, error)

Events returns the latest events, as they were posted, newest first. limit is 1 to 100; zero means the API's default of 25.

func (*SubscriptionsService) Get

Get returns one subscription. It returns an error matching ErrNotFound when the subscription does not exist or belongs to another key.

func (*SubscriptionsService) List

List returns one page of your subscriptions, newest first.

func (*SubscriptionsService) Pause

Pause pauses polling and deliveries; SubscriptionsService.Resume picks up where it left off.

func (*SubscriptionsService) Resume

Resume resumes a paused or suspended subscription. A completed one cannot be resumed and returns an error matching ErrBadRequest.

func (*SubscriptionsService) RotateSecret

func (s *SubscriptionsService) RotateSecret(ctx context.Context, id string) (string, error)

RotateSecret replaces the webhook signing secret and returns the new one. Deliveries are signed with the new secret from now on, so update your receiver right away.

func (*SubscriptionsService) Secret

func (s *SubscriptionsService) Secret(ctx context.Context, id string) (string, error)

Secret returns the full webhook signing secret.

func (*SubscriptionsService) SendTest

func (s *SubscriptionsService) SendTest(ctx context.Context, id string) (string, error)

SendTest sends a subscription.test webhook and returns its event id.

func (*SubscriptionsService) Update

Update changes a subscription. Only the fields set in params change.

type TimelineEntry

type TimelineEntry struct {
	// OccurredAt is when it happened.
	OccurredAt time.Time `json:"occurredAt"`
	// Title is the title, for example "Time Adjustment".
	Title string `json:"title"`
	// Source is the source, for example "Airline".
	Source string `json:"source"`
	// Events describes what changed, as sentences.
	Events []string `json:"events"`
}

TimelineEntry is one entry of a flight's upstream history.

type UpdateSubscriptionParams

type UpdateSubscriptionParams struct {
	// WebhookURL moves deliveries to another URL.
	WebhookURL string `json:"webhookUrl,omitempty"`
	// Events replaces the change type filter.
	Events []FlightChangeType `json:"events,omitempty"`
	// DelayThresholdMinutes replaces the delay threshold.
	DelayThresholdMinutes *int `json:"delayThresholdMinutes,omitempty"`
	// Headers replaces the delivery headers. The API cannot clear them, so an
	// empty map leaves them as they are.
	Headers map[string]string `json:"headers,omitempty"`
	// Metadata replaces the metadata. The API cannot clear it, so an empty map
	// leaves it as it is.
	Metadata map[string]string `json:"metadata,omitempty"`
}

UpdateSubscriptionParams changes a subscription. Zero values are left as they are.

type VerifyOption

type VerifyOption func(*verifyConfig)

VerifyOption configures VerifyWebhook and the functions built on it.

func WithErrorHandler

func WithErrorHandler(report func(r *http.Request, event *WebhookEvent, err error)) VerifyOption

WithErrorHandler sets what WebhookHandler does with an error handle returns, after answering 500 so the API retries the delivery. The default logs it with the log package. VerifyWebhook and ConstructWebhookEvent ignore it.

func WithNow

func WithNow(now time.Time) VerifyOption

WithNow checks the signing time against now instead of the current time. Useful in tests.

func WithTolerance

func WithTolerance(tolerance time.Duration) VerifyOption

WithTolerance sets how far the signing time may be from now. Zero skips the check. The default is DefaultWebhookTolerance.

type WebhookEvent

type WebhookEvent struct {
	// ID is the event id, "evt_<uuid>". It is the same across delivery
	// retries: deduplicate on it.
	ID string `json:"id"`
	// Type is the event type.
	Type WebhookEventType `json:"type"`
	// CreatedAt is when the event was created.
	CreatedAt time.Time `json:"createdAt"`
	// SubscriptionID is the subscription it belongs to.
	SubscriptionID string `json:"subscriptionId"`
	// FlightKey is the flight's key.
	FlightKey string `json:"flightKey"`
	// Flight is the flight after the change. Nil on some lifecycle events.
	Flight *Flight `json:"flight,omitempty"`
	// Changes says what changed. Empty except on flight.updated.
	Changes []FlightChange `json:"changes"`
	// PreviousFetchedAt is when the previous snapshot was fetched, on
	// flight.updated.
	PreviousFetchedAt *time.Time `json:"previousFetchedAt,omitempty"`
	// Metadata is the subscription's metadata.
	Metadata map[string]string `json:"metadata,omitempty"`
}

WebhookEvent is a webhook event, as posted to your receiver and as listed by SubscriptionsService.Events.

func ConstructWebhookEvent

func ConstructWebhookEvent(payload []byte, signature, secret string, opts ...VerifyOption) (*WebhookEvent, error)

ConstructWebhookEvent verifies the signature, as VerifyWebhook does, and decodes the event.

It returns a *WebhookSignatureError when the signature does not verify, ErrEmptySecret when secret is empty, and a decoding error when the verified body is not a webhook event.

type WebhookEventType

type WebhookEventType string

WebhookEventType is the type of a webhook event.

const (
	// EventSubscriptionCreated: the subscription was created.
	EventSubscriptionCreated WebhookEventType = "subscription.created"
	// EventSubscriptionActivated: a pending subscription found its flight.
	EventSubscriptionActivated WebhookEventType = "subscription.activated"
	// EventSubscriptionTest: a test event sent with SubscriptionsService.SendTest.
	EventSubscriptionTest WebhookEventType = "subscription.test"
	// EventSubscriptionCompleted: the flight finished and the subscription
	// completed.
	EventSubscriptionCompleted WebhookEventType = "subscription.completed"
	// EventSubscriptionSuspended: the subscription was suspended.
	EventSubscriptionSuspended WebhookEventType = "subscription.suspended"
	// EventFlightUpdated: something about the flight changed; see the event's
	// Changes.
	EventFlightUpdated WebhookEventType = "flight.updated"
)

Webhook event types.

func (WebhookEventType) Known

func (t WebhookEventType) Known() bool

Known reports whether t is one of the constants above.

type WebhookSignatureError

type WebhookSignatureError struct {
	// Reason says why verification failed.
	Reason string
}

WebhookSignatureError is returned when a webhook signature does not verify.

func (*WebhookSignatureError) Error

func (e *WebhookSignatureError) Error() string

Error describes why verification failed.

func (*WebhookSignatureError) Is

func (e *WebhookSignatureError) Is(target error) bool

Is reports whether target is ErrInvalidSignature.

Directories

Path Synopsis
examples
lookup command
Command lookup looks a flight up and prints its status.
Command lookup looks a flight up and prints its status.
webhook-server command
Command webhook-server receives FlightByte webhooks and logs each change.
Command webhook-server receives FlightByte webhooks and logs each change.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL