Documentation
¶
Overview ¶
Package ifax is the official Go client for the INeedToSendAFax API.
Send faxes programmatically, track their status, and verify webhook callbacks. The client is safe for concurrent use.
Create a client with your API key and send a fax:
client := ifax.New(os.Getenv("IFAX_API_KEY"))
fax, err := client.SendFax(ctx, &ifax.SendFaxParams{
To: "15551234567",
Files: []ifax.File{ifax.FilePath("invoice.pdf")},
})
See https://api.ineedtosendafax.com/docs for the API reference.
Index ¶
- Constants
- Variables
- func IsInsufficientCredits(err error) bool
- func IsNotFound(err error) bool
- func IsRateLimited(err error) bool
- func VerifyWebhook(secret, signatureHeader string, body []byte, tolerance time.Duration) error
- type APIError
- type Account
- type Client
- func (c *Client) GetFax(ctx context.Context, id string) (*Fax, error)
- func (c *Client) ListFaxes(ctx context.Context, limit int) (*FaxList, error)
- func (c *Client) Me(ctx context.Context) (*Account, error)
- func (c *Client) Pricing(ctx context.Context) (*Pricing, error)
- func (c *Client) SendFax(ctx context.Context, p *SendFaxParams) (*Fax, error)
- type Event
- type Fax
- type FaxList
- type File
- type Option
- type Pricing
- type SendFaxParams
Examples ¶
Constants ¶
const DefaultBaseURL = "https://api.ineedtosendafax.com"
DefaultBaseURL is the production API endpoint.
const DefaultTolerance = 5 * time.Minute
DefaultTolerance is the recommended replay window for webhook timestamps.
const Version = "1.0.0"
Version is the SDK version.
Variables ¶
var ( ErrNotFound = &APIError{StatusCode: http.StatusNotFound, Code: "not_found"} ErrInsufficientCredits = &APIError{StatusCode: http.StatusPaymentRequired, Code: "insufficient_credits"} ErrRateLimited = &APIError{StatusCode: http.StatusTooManyRequests, Code: "rate_limited"} ErrInvalidKey = &APIError{StatusCode: http.StatusUnauthorized, Code: "invalid_key"} )
Sentinel API errors for use with errors.Is.
var ( ErrMalformedSignatureHeader = errors.New("ifax: malformed X-Fax-Signature header") ErrInvalidSignature = errors.New("ifax: webhook signature does not match") ErrTimestampOutsideTolerance = errors.New("ifax: webhook timestamp outside tolerance") )
Webhook verification errors.
var ErrFileTooLarge = errors.New("ifax: file exceeds 32 MB")
ErrFileTooLarge is returned when a file exceeds 32 MB.
var ErrNoAPIKey = errors.New("ifax: no API key (set IFAX_API_KEY)")
ErrNoAPIKey is returned by NewFromEnv when no key is present.
Functions ¶
func IsInsufficientCredits ¶
IsInsufficientCredits reports whether err is a 402 (out of credit).
func IsNotFound ¶
IsNotFound reports whether err is a 404 from the API.
func VerifyWebhook ¶
VerifyWebhook checks the X-Fax-Signature header against the raw request body using your webhook signing secret. Pass the header value verbatim and the unmodified body. A tolerance of 0 uses DefaultTolerance; use a negative value to skip the timestamp check.
Return values are ErrMalformedSignatureHeader, ErrTimestampOutsideTolerance, or ErrInvalidSignature, so callers can distinguish a bad shape from a bad signature from an old replay.
Example ¶
ExampleVerifyWebhook verifies a signed callback, then parses it.
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
ifax "github.com/INeedToSendAFax/sdk-go"
)
func main() {
const secret = "whsec_example"
body := []byte(`{"id":"abc","status":"sent"}`)
ts := "1700000000"
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(ts + "."))
mac.Write(body)
header := "t=" + ts + ",v1=" + hex.EncodeToString(mac.Sum(nil))
// A negative tolerance skips the timestamp/replay check for this example.
if err := ifax.VerifyWebhook(secret, header, body, -1); err != nil {
fmt.Println("invalid:", err)
return
}
event, _ := ifax.ParseEvent(body)
fmt.Println("verified", event.ID, event.Status)
}
Output: verified abc sent
Example (Stale) ¶
ExampleVerifyWebhook_stale rejects an old signature to prevent replays.
package main
import (
"fmt"
"time"
ifax "github.com/INeedToSendAFax/sdk-go"
)
func main() {
const secret = "whsec_example"
body := []byte(`{"id":"abc"}`)
header := "t=1700000000,v1=0000"
err := ifax.VerifyWebhook(secret, header, body, 5*time.Minute)
fmt.Println(err == ifax.ErrTimestampOutsideTolerance)
}
Output: true
Types ¶
type APIError ¶
type APIError struct {
StatusCode int
Code string
Message string
// RetryAfter is set when the API returns a Retry-After header.
RetryAfter time.Duration
}
APIError is returned for any non-2xx API response.
type Account ¶
type Account struct {
KeyPrefix string `json:"key_prefix"`
Email string `json:"email"`
BalanceCents int `json:"balance_cents"`
PriceCents int `json:"price_cents"`
MaxPages int `json:"max_pages"`
RateLimitPerMin int `json:"rate_limit_per_min"`
DailyCap int `json:"daily_cap"`
FaxesLast24h int `json:"faxes_last_24h"`
CallbackSecretSet bool `json:"callback_secret_set"`
}
Account is the response from GET /v1/me.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client talks to the INeedToSendAFax API. It is safe for concurrent use; do not modify its fields directly. Construct it with New.
func NewFromEnv ¶
NewFromEnv builds a Client from the IFAX_API_KEY environment variable.
func (*Client) GetFax ¶
GetFax returns a single fax by id. A 404 is returned as an *APIError that satisfies IsNotFound.
Example ¶
ExampleClient_GetFax polls a fax for its final status.
package main
import (
"context"
"fmt"
"os"
ifax "github.com/INeedToSendAFax/sdk-go"
)
func main() {
client := ifax.New(os.Getenv("IFAX_API_KEY"))
fax, err := client.GetFax(context.Background(), "b1a2c3d4-0000-0000-0000-000000000000")
if err != nil {
fmt.Println("error:", err)
return
}
if fax.Status == "sent" {
fmt.Println("delivered")
}
}
Output:
func (*Client) ListFaxes ¶
ListFaxes returns recent faxes for the account. limit is clamped by the API to 1..200; pass 0 for the default (50).
func (*Client) SendFax ¶
SendFax submits a fax. It returns a Fax with status "queued"; poll GetFax or use a callback_url to learn when it is delivered. A retried call is safe: if IdempotencyKey is empty one is generated automatically.
Example ¶
ExampleClient_SendFax shows how to send a fax with a callback.
package main
import (
"context"
"fmt"
"os"
ifax "github.com/INeedToSendAFax/sdk-go"
)
func main() {
client := ifax.New(os.Getenv("IFAX_API_KEY"))
fax, err := client.SendFax(context.Background(), &ifax.SendFaxParams{
To: "15551234567",
Files: []ifax.File{ifax.FilePath("invoice.pdf")},
CallbackURL: "https://example.com/fax-webhook",
})
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println("queued:", fax.ID)
}
Output:
type Event ¶
type Event struct {
ID string `json:"id"`
Status string `json:"status"`
To string `json:"to"`
Pages int `json:"pages"`
Bitrate int `json:"bitrate"`
Mode string `json:"mode"`
Error string `json:"error"`
CompletedAt *time.Time `json:"completed_at"`
}
Event is the JSON body of a fax status callback.
func ParseEvent ¶
ParseEvent decodes a callback body. Call VerifyWebhook first when the request came from the network.
type Fax ¶
type Fax struct {
ID string `json:"id"`
Status string `json:"status"`
To string `json:"to"`
Pages int `json:"pages"`
PriceCents int `json:"price_cents"`
Bitrate int `json:"bitrate"`
Mode string `json:"mode"`
Error string `json:"error"`
CreatedAt *time.Time `json:"created_at"`
CompletedAt *time.Time `json:"completed_at"`
}
Fax is a fax job.
type File ¶
type File struct {
Filename string
// contains filtered or unexported fields
}
File is a document to fax. Create one with FilePath, FileBytes or FileReader. Files are read into memory when the fax is sent so that retries can replay the request body.
type Option ¶
type Option func(*Client)
Option configures a Client.
func WithBaseURL ¶
WithBaseURL overrides the API base URL (useful for testing).
func WithHTTPClient ¶
WithHTTPClient uses a custom *http.Client.
func WithMaxRetries ¶
WithMaxRetries sets how many times transient failures (429 and 5xx responses, network errors) are retried. The default is 2.
func WithTimeout ¶
WithTimeout sets the request timeout on a default HTTP client. It is ignored when WithHTTPClient is also supplied.
type Pricing ¶
type Pricing struct {
PriceCents int `json:"price_cents"`
Currency string `json:"currency"`
MaxPages int `json:"max_pages"`
MinCreditCents int `json:"min_credit_cents"`
Countries []string `json:"countries"`
CoverAvailable bool `json:"cover_available"`
Callbacks bool `json:"callbacks"`
IdempotencyHeader string `json:"idempotency_header"`
}
Pricing is the response from GET /v1/pricing.
type SendFaxParams ¶
type SendFaxParams struct {
To string
Files []File
CallbackURL string
Cover bool
CoverText string
RecipientName string
RecipientCompany string
SenderName string
SenderCompany string
SenderPhone string
// IdempotencyKey makes retries safe. If empty, one is generated
// automatically (a retried send will not deliver the fax twice).
IdempotencyKey string
}
SendFaxParams describes a fax to send. To and Files are required.