README.md
December 18, 2025 ยท View on GitHub
Go Invoice Ninja SDK
A professional, idiomatic Go SDK for the Invoice Ninja API. This SDK provides a clean interface for interacting with Invoice Ninja's comprehensive invoicing and payment platform.
โจ Features
- ๐ Secure Authentication - Token-based API authentication
- ๐ณ Payment Management - Full CRUD operations with refund support
- ๐ Invoice Operations - Create, send, and manage invoices
- ๐ฅ Client Management - Client CRUD with merge capabilities
- ๐ฐ Credits & Payment Terms - Complete credit and terms management
- ๐ฅ File Operations - Download PDFs and upload documents
- ๐ Webhook Handling - Built-in handler with signature verification
- โก Rate Limiting - Client-side limiting with automatic retry
- ๐ Retry Logic - Exponential backoff for transient failures
- ๐ Self-hosted Support - Works with cloud and self-hosted instances
- โ Fully Tested - 90+ tests with comprehensive coverage
๐ฆ Installation
go get github.com/AshkanYarmoradi/go-invoice-ninja
๐ Documentation
๐๏ธ Project Structure
go-invoice-ninja/
โโโ .github/workflows/ # CI/CD pipelines
โโโ docs/ # Detailed documentation
โโโ examples/ # Runnable examples
โ โโโ basic/ # Basic usage
โ โโโ invoices/ # Invoice operations
โ โโโ webhooks/ # Webhook handling
โโโ testdata/ # Test fixtures
โ
โโโ client.go # Main client
โโโ clients.go # Clients service
โโโ credits.go # Credits service
โโโ errors.go # Error types
โโโ files.go # File operations
โโโ invoices.go # Invoices service
โโโ models.go # Data models
โโโ payments.go # Payments service
โโโ payment_terms.go # Payment terms
โโโ retry.go # Retry & rate limiting
โโโ webhooks.go # Webhook handling
โ
โโโ CHANGELOG.md # Version history
โโโ CONTRIBUTING.md # Contribution guide
โโโ LICENSE # MIT License
โโโ Makefile # Build automation
โโโ README.md # This file
๐ Quick Start
package main
import (
"context"
"fmt"
"log"
invoiceninja "github.com/AshkanYarmoradi/go-invoice-ninja"
)
func main() {
// Create a new client
client := invoiceninja.NewClient("your-api-token")
// For self-hosted instances:
// client := invoiceninja.NewClient("your-api-token",
// invoiceninja.WithBaseURL("https://your-instance.com"))
ctx := context.Background()
// List payments
payments, err := client.Payments.List(ctx, &invoiceninja.PaymentListOptions{
PerPage: 10,
Page: 1,
})
if err != nil {
log.Fatal(err)
}
for _, payment := range payments.Data {
fmt.Printf("Payment %s: $%.2f\n", payment.Number, payment.Amount)
}
}
๐ Authentication
All API requests require an API token. You can obtain your token from: Settings > Account Management > Integrations > API tokens
client := invoiceninja.NewClient("your-api-token")
โ๏ธ Configuration Options
// Custom HTTP client
client := invoiceninja.NewClient("token",
invoiceninja.WithHTTPClient(customHTTPClient))
// Custom base URL (for self-hosted)
client := invoiceninja.NewClient("token",
invoiceninja.WithBaseURL("https://your-instance.com"))
// Custom timeout
client := invoiceninja.NewClient("token",
invoiceninja.WithTimeout(60 * time.Second))
๐ณ Payments
List Payments
payments, err := client.Payments.List(ctx, &invoiceninja.PaymentListOptions{
PerPage: 20,
Page: 1,
ClientID: "client-hash-id",
Status: "active",
Sort: "amount|desc",
})
Get Payment
payment, err := client.Payments.Get(ctx, "payment-hash-id")
Create Payment
payment, err := client.Payments.Create(ctx, &invoiceninja.PaymentRequest{
ClientID: "client-hash-id",
Amount: 100.00,
Date: "2024-01-15",
Invoices: []invoiceninja.PaymentInvoice{
{InvoiceID: "invoice-hash-id", Amount: 100.00},
},
})
Update Payment
payment, err := client.Payments.Update(ctx, "payment-hash-id", &invoiceninja.PaymentRequest{
PrivateNotes: "Updated notes",
})
Delete Payment
err := client.Payments.Delete(ctx, "payment-hash-id")
Refund Payment
payment, err := client.Payments.Refund(ctx, &invoiceninja.RefundRequest{
ID: "payment-hash-id",
Amount: 50.00,
GatewayRefund: true,
})
Bulk Actions
// Archive multiple payments
payments, err := client.Payments.Bulk(ctx, "archive", []string{"id1", "id2"})
// Single item convenience methods
payment, err := client.Payments.Archive(ctx, "payment-hash-id")
payment, err := client.Payments.Restore(ctx, "payment-hash-id")
Invoices
List Invoices
invoices, err := client.Invoices.List(ctx, &invoiceninja.InvoiceListOptions{
PerPage: 20,
ClientID: "client-hash-id",
})
Get Invoice
invoice, err := client.Invoices.Get(ctx, "invoice-hash-id")
Create Invoice
invoice, err := client.Invoices.Create(ctx, &invoiceninja.Invoice{
ClientID: "client-hash-id",
LineItems: []invoiceninja.LineItem{
{ProductKey: "Product A", Quantity: 2, Cost: 50.00},
},
})
Invoice Actions
// Mark as paid
invoice, err := client.Invoices.MarkPaid(ctx, "invoice-hash-id")
// Mark as sent
invoice, err := client.Invoices.MarkSent(ctx, "invoice-hash-id")
// Send via email
invoice, err := client.Invoices.Email(ctx, "invoice-hash-id")
Clients
List Clients
clients, err := client.Clients.List(ctx, &invoiceninja.ClientListOptions{
PerPage: 20,
Balance: "gt:1000", // Balance greater than 1000
Include: "contacts,documents",
})
Create Client
newClient, err := client.Clients.Create(ctx, &invoiceninja.INClient{
Name: "Acme Corporation",
Contacts: []invoiceninja.ClientContact{
{
FirstName: "John",
LastName: "Doe",
Email: "john@acme.com",
IsPrimary: true,
},
},
})
Merge Clients
mergedClient, err := client.Clients.Merge(ctx, "primary-id", "mergeable-id")
Payment Terms
// List payment terms
terms, err := client.PaymentTerms.List(ctx, nil)
// Create a payment term
term, err := client.PaymentTerms.Create(ctx, &invoiceninja.PaymentTerm{
Name: "Net 45",
NumDays: 45,
})
// Get, Update, Delete
term, err := client.PaymentTerms.Get(ctx, "term-id")
term, err := client.PaymentTerms.Update(ctx, "term-id", &invoiceninja.PaymentTerm{Name: "Net 60"})
err := client.PaymentTerms.Delete(ctx, "term-id")
Credits
// List credits
credits, err := client.Credits.List(ctx, &invoiceninja.CreditListOptions{
ClientID: "client-hash-id",
PerPage: 20,
})
// Create a credit
credit, err := client.Credits.Create(ctx, &invoiceninja.Credit{
ClientID: "client-hash-id",
LineItems: []invoiceninja.LineItem{
{ProductKey: "Credit", Quantity: 1, Cost: 100.00},
},
})
// Credit actions
credit, err := client.Credits.MarkSent(ctx, "credit-id")
credit, err := client.Credits.Email(ctx, "credit-id")
File Downloads
// Download invoice PDF
pdf, err := client.Downloads.DownloadInvoicePDF(ctx, "invitation-key")
// Download delivery note
pdf, err := client.Downloads.DownloadInvoiceDeliveryNote(ctx, "invoice-id")
// Download credit PDF
pdf, err := client.Downloads.DownloadCreditPDF(ctx, "invitation-key")
// Save to file
os.WriteFile("invoice.pdf", pdf, 0644)
File Uploads
// Upload document to invoice
err := client.Uploads.UploadInvoiceDocument(ctx, "invoice-id", "/path/to/file.pdf")
// Upload to other entities
err := client.Uploads.UploadPaymentDocument(ctx, "payment-id", "/path/to/file.pdf")
err := client.Uploads.UploadClientDocument(ctx, "client-id", "/path/to/file.pdf")
err := client.Uploads.UploadCreditDocument(ctx, "credit-id", "/path/to/file.pdf")
// Upload from io.Reader
reader := bytes.NewReader(pdfContent)
err := client.Uploads.UploadDocumentFromReader(ctx, "invoices", "invoice-id", "document.pdf", reader)
Webhooks
Handle incoming webhooks from Invoice Ninja:
// Create a webhook handler
handler := invoiceninja.NewWebhookHandler("your-webhook-secret")
// Register event handlers
handler.OnPaymentCreated(func(event *invoiceninja.WebhookEvent) error {
payment, err := event.ParsePayment()
if err != nil {
return err
}
fmt.Printf("New payment: %s ($%.2f)\n", payment.Number, payment.Amount)
return nil
})
handler.OnInvoiceCreated(func(event *invoiceninja.WebhookEvent) error {
invoice, err := event.ParseInvoice()
if err != nil {
return err
}
fmt.Printf("New invoice: %s\n", invoice.Number)
return nil
})
// Use as HTTP handler
http.Handle("/webhook", handler)
http.ListenAndServe(":8080", nil)
Supported webhook events:
OnInvoiceCreated,OnInvoiceUpdated,OnInvoiceDeletedOnPaymentCreated,OnPaymentUpdated,OnPaymentDeletedOnClientCreated,OnClientUpdatedOnCreditCreated,OnQuoteCreated
Rate Limiting & Retry
For production use, use the rate-limited client with automatic retries:
// Create a rate-limited client
client := invoiceninja.NewRateLimitedClient("your-api-token",
invoiceninja.WithBaseURL("https://your-instance.com"))
// Configure rate limit (requests per second)
client.SetRateLimit(10)
// Configure retry behavior
client.SetRetryConfig(&invoiceninja.RetryConfig{
MaxRetries: 3,
InitialBackoff: 1 * time.Second,
MaxBackoff: 30 * time.Second,
BackoffMultiplier: 2.0,
RetryOnStatusCodes: []int{429, 500, 502, 503, 504},
Jitter: true,
})
Generic Requests
For API endpoints not covered by specialized methods, use the generic request:
// GET request
var activities json.RawMessage
err := client.Request(ctx, "GET", "/api/v1/activities", nil, &activities)
// POST request with body
body := map[string]interface{}{
"name": "New Product",
"cost": 99.99,
}
var result json.RawMessage
err := client.Request(ctx, "POST", "/api/v1/products", body, &result)
// With query parameters
query := url.Values{}
query.Set("per_page", "50")
err := client.RequestWithQuery(ctx, "GET", "/api/v1/products", query, nil, &result)
Error Handling
The SDK provides typed errors with helper methods:
payment, err := client.Payments.Get(ctx, "invalid-id")
if err != nil {
if apiErr, ok := invoiceninja.IsAPIError(err); ok {
if apiErr.IsNotFound() {
fmt.Println("Payment not found")
} else if apiErr.IsUnauthorized() {
fmt.Println("Invalid API token")
} else if apiErr.IsValidationError() {
fmt.Printf("Validation errors: %v\n", apiErr.Errors)
} else if apiErr.IsRateLimited() {
fmt.Println("Rate limit exceeded, please wait")
}
}
log.Fatal(err)
}
๐งช Testing
# Run all tests
make test
# Run with race detector
make test-race
# Run with coverage
make coverage
# Run linter
make lint
๐ Integration Tests
Run integration tests against a live Invoice Ninja server:
# Run against demo server
go test -tags=integration -v ./...
# Run against custom server
INVOICE_NINJA_BASE_URL=https://your-server.com \
INVOICE_NINJA_API_TOKEN=your-token \
go test -tags=integration -v ./...
๐ Examples
Check out the examples directory for complete working examples:
- Basic Usage - Getting started
- Invoice Management - Creating and managing invoices
- Webhook Handling - Setting up webhook handlers
๐ API Reference
| Status Code | Description |
|---|---|
| 200 | Success |
| 400 | Bad Request |
| 401 | Unauthorized - Invalid API token |
| 403 | Forbidden - No permission |
| 404 | Not Found |
| 422 | Validation Error |
| 429 | Rate Limited |
| 5xx | Server Error |
๐ License
This SDK is released under the MIT License.
๐ค Contributing
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Write tests for your changes
- Ensure all tests pass (
make test) - Run the linter (
make lint) - Commit your changes (
git commit -m 'feat: add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
๐ Support
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ
โ โโโโโโโ โโโโโโโ โ
โ โโโโโโโโ โโโโโโโโโ โ
โ โโโ โโโโโโโ โโโ โ
โ โโโ โโโโโโ โโโ โ
โ โโโโโโโโโโโโโโโโโโ โ
โ โโโโโโโ โโโโโโโ โ
โ โ
โ โโโโโโโ โโโโโโ โโโ โโโโโโโ โโโ โโโโโโโโโโโโโโโ โ
โ โโโโโโโโ โโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โโโโโโโโโ โโโโโโ โโโโโโ โโโโโโโโโ โโโโโโ โ
โ โโโโโโโโโโโโโโโโโ โโโโโโโ โโโโโโโโโ โโโโโโ โ
โ โโโโโโ โโโโโโ โโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โโโโโโ โโโโโ โโโโโ โโโโโโโ โโโ โโโโโโโโโโโโโโโ โ
โ โ
โ โโโโ โโโโโโโโโโ โโโ โโโ โโโโโโ โ
โ โโโโโ โโโโโโโโโโโ โโโ โโโโโโโโโโโ โ
โ โโโโโโ โโโโโโโโโโโโ โโโ โโโโโโโโโโโ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโ โ
โ โโโ โโโโโโโโโโโโ โโโโโโโโโโโโโโโโโ โโโ โ
โ โโโ โโโโโโโโโโโ โโโโโ โโโโโโ โโโ โโโ โ
โ โ
โ โญ Star us on GitHub! โญ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Made with โค๏ธ by Ashkan Yarmoradi