AshkanYarmoradi/go-invoice-ninja

Go SDK for Invoice Ninja API - payments, invoices, clients, webhooks with retry & rate limiting

49

stars

15

commits

Go

primary language

Jan 17, 2026

updated

README

Go Invoice Ninja SDK

Go Invoice Ninja SDK

Go Reference Go Report Card CI codecov License: MIT

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, OnInvoiceDeleted
  • OnPaymentCreated, OnPaymentUpdated, OnPaymentDeleted
  • OnClientCreated, OnClientUpdated
  • OnCreditCreated, 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:

πŸ“‹ API Reference

Status CodeDescription
200Success
400Bad Request
401Unauthorized - Invalid API token
403Forbidden - No permission
404Not Found
422Validation Error
429Rate Limited
5xxServer Error

πŸ“„ License

This SDK is released under the MIT License.

🀝 Contributing

Contributions are welcome! Please see CONTRIBUTING.md for guidelines.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Write tests for your changes
  4. Ensure all tests pass (make test)
  5. Run the linter (make lint)
  6. Commit your changes (git commit -m 'feat: add amazing feature')
  7. Push to the branch (git push origin feature/amazing-feature)
  8. Open a Pull Request

πŸ“ž Support


Go Invoice Ninja Logo

                    ╔═══════════════════════════════════════════════════════════╗
                    β•‘                                                           β•‘
                    β•‘             β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—                              β•‘
                    β•‘            β–ˆβ–ˆβ•”β•β•β•β•β• β–ˆβ–ˆβ•”β•β•β•β–ˆβ–ˆβ•—                             β•‘
                    β•‘            β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘                             β•‘
                    β•‘            β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘                             β•‘
                    β•‘            β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•                             β•‘
                    β•‘             β•šβ•β•β•β•β•β•  β•šβ•β•β•β•β•β•                              β•‘
                    β•‘                                                           β•‘
                    β•‘    β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ•—   β–ˆβ–ˆβ•—β–ˆβ–ˆβ•—   β–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—     β•‘
                    β•‘    β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β•β•β•β–ˆβ–ˆβ•”β•β•β•β•β•     β•‘
                    β•‘    β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β–ˆβ–ˆβ•— β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘     β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—       β•‘
                    β•‘    β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•— β–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘     β–ˆβ–ˆβ•”β•β•β•       β•‘
                    β•‘    β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β•šβ–ˆβ–ˆβ–ˆβ–ˆβ•‘ β•šβ–ˆβ–ˆβ–ˆβ–ˆβ•”β• β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—     β•‘
                    β•‘    β•šβ•β•β•šβ•β•  β•šβ•β•β•β•  β•šβ•β•β•β•   β•šβ•β•β•β•β•β• β•šβ•β• β•šβ•β•β•β•β•β•β•šβ•β•β•β•β•β•β•     β•‘
                    β•‘                                                           β•‘
                    β•‘    β–ˆβ–ˆβ–ˆβ•—   β–ˆβ–ˆβ•—β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ•—   β–ˆβ–ˆβ•—     β–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—                 β•‘
                    β•‘    β–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ•‘     β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—                β•‘
                    β•‘    β–ˆβ–ˆβ•”β–ˆβ–ˆβ•— β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β–ˆβ–ˆβ•— β–ˆβ–ˆβ•‘     β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘                β•‘
                    β•‘    β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β–ˆβ–ˆ   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•‘                β•‘
                    β•‘    β–ˆβ–ˆβ•‘ β•šβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β•šβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘                β•‘
                    β•‘    β•šβ•β•  β•šβ•β•β•β•β•šβ•β•β•šβ•β•  β•šβ•β•β•β• β•šβ•β•β•β•β• β•šβ•β•  β•šβ•β•                β•‘
                    β•‘                                                           β•‘
                    β•‘           ⭐ Star us on GitHub! ⭐                       β•‘
                    β•‘                                                           β•‘
                    β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

Made with ❀️ by Ashkan Yarmoradi

Contributors

AshkanYarmoradi

15 commits

AshkanYarmoradi/go-invoice-ninja

Go SDK for Invoice Ninja API - payments, invoices, clients, webhooks with retry & rate limiting

49

stars

15

commits

Go

primary language

Jan 17, 2026

updated

README

Go Invoice Ninja SDK

Go Invoice Ninja SDK

Go Reference Go Report Card CI codecov License: MIT

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, OnInvoiceDeleted
  • OnPaymentCreated, OnPaymentUpdated, OnPaymentDeleted
  • OnClientCreated, OnClientUpdated
  • OnCreditCreated, 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:

πŸ“‹ API Reference

Status CodeDescription
200Success
400Bad Request
401Unauthorized - Invalid API token
403Forbidden - No permission
404Not Found
422Validation Error
429Rate Limited
5xxServer Error

πŸ“„ License

This SDK is released under the MIT License.

🀝 Contributing

Contributions are welcome! Please see CONTRIBUTING.md for guidelines.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Write tests for your changes
  4. Ensure all tests pass (make test)
  5. Run the linter (make lint)
  6. Commit your changes (git commit -m 'feat: add amazing feature')
  7. Push to the branch (git push origin feature/amazing-feature)
  8. Open a Pull Request

πŸ“ž Support


Go Invoice Ninja Logo

                    ╔═══════════════════════════════════════════════════════════╗
                    β•‘                                                           β•‘
                    β•‘             β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—                              β•‘
                    β•‘            β–ˆβ–ˆβ•”β•β•β•β•β• β–ˆβ–ˆβ•”β•β•β•β–ˆβ–ˆβ•—                             β•‘
                    β•‘            β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘                             β•‘
                    β•‘            β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘                             β•‘
                    β•‘            β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•                             β•‘
                    β•‘             β•šβ•β•β•β•β•β•  β•šβ•β•β•β•β•β•                              β•‘
                    β•‘                                                           β•‘
                    β•‘    β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ•—   β–ˆβ–ˆβ•—β–ˆβ–ˆβ•—   β–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—     β•‘
                    β•‘    β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β•β•β•β–ˆβ–ˆβ•”β•β•β•β•β•     β•‘
                    β•‘    β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β–ˆβ–ˆβ•— β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘     β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—       β•‘
                    β•‘    β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•— β–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘     β–ˆβ–ˆβ•”β•β•β•       β•‘
                    β•‘    β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β•šβ–ˆβ–ˆβ–ˆβ–ˆβ•‘ β•šβ–ˆβ–ˆβ–ˆβ–ˆβ•”β• β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—     β•‘
                    β•‘    β•šβ•β•β•šβ•β•  β•šβ•β•β•β•  β•šβ•β•β•β•   β•šβ•β•β•β•β•β• β•šβ•β• β•šβ•β•β•β•β•β•β•šβ•β•β•β•β•β•β•     β•‘
                    β•‘                                                           β•‘
                    β•‘    β–ˆβ–ˆβ–ˆβ•—   β–ˆβ–ˆβ•—β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ•—   β–ˆβ–ˆβ•—     β–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—                 β•‘
                    β•‘    β–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ•‘     β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—                β•‘
                    β•‘    β–ˆβ–ˆβ•”β–ˆβ–ˆβ•— β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β–ˆβ–ˆβ•— β–ˆβ–ˆβ•‘     β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘                β•‘
                    β•‘    β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β–ˆβ–ˆ   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•‘                β•‘
                    β•‘    β–ˆβ–ˆβ•‘ β•šβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β•šβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘                β•‘
                    β•‘    β•šβ•β•  β•šβ•β•β•β•β•šβ•β•β•šβ•β•  β•šβ•β•β•β• β•šβ•β•β•β•β• β•šβ•β•  β•šβ•β•                β•‘
                    β•‘                                                           β•‘
                    β•‘           ⭐ Star us on GitHub! ⭐                       β•‘
                    β•‘                                                           β•‘
                    β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

Made with ❀️ by Ashkan Yarmoradi

Contributors

AshkanYarmoradi

15 commits

Languages

Go

98.4%

Makefile

1.6%