fulkruma

package module
v0.1.0 Latest Latest
Warning

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

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

README

fulkruma (Go)

Official Go SDK for Fulkruma — stock, warehouses, shipping (Biteship), licenses, deliveries, API keys, audit log, billing, integrations status, stats, webhooks. Mirrors the Node (@forjio/fulkruma-node) and Python (fulkruma) SDKs 1:1.

Install

go get github.com/hachimi-cat/fulkruma-go

Requires Go 1.22+. No third-party dependencies — pure stdlib.

Quickstart

Env vars (or pass them to NewClient):

FULKRUMA_KEY_ID=AKIAFULK...
FULKRUMA_SECRET=...
FULKRUMA_BASE_URL=https://fulkruma.com    # optional, this is the default
FULKRUMA_ON_BEHALF_OF=acc_...             # optional, platform-admin only
List products
package main

import (
    "context"
    "fmt"

    fulkruma "github.com/hachimi-cat/fulkruma-go"
)

func main() {
    c, err := fulkruma.NewClient(fulkruma.ClientOptions{})
    if err != nil { panic(err) }

    products, err := c.Products.List(context.Background(), fulkruma.ProductListParams{})
    if err != nil { panic(err) }
    for _, p := range products {
        fmt.Printf("%s — %s\n", p.ID, p.Name)
    }
}
Verify an inbound webhook
http.HandleFunc("/webhooks/fulkruma", func(w http.ResponseWriter, r *http.Request) {
    body, _ := io.ReadAll(r.Body)
    event, err := fulkruma.VerifyWebhook(
        body,
        r.Header.Get("Fulkruma-Signature"),
        os.Getenv("FULKRUMA_WEBHOOK_SECRET"),
        nil,
    )
    if err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }
    log.Printf("got %s event %s", event.Type, event.ID)
    w.WriteHeader(204)
})
Platform admin (Pattern 2)
// One client per request, scoped to a merchant.
scoped := c.ForMerchant("acc_merchant_123")
warehouses, _ := scoped.Warehouses.List(ctx)

// Or override per call:
scoped, _ := c.Warehouses.List(ctx)  // not really shown — Warehouses.List doesn't take OBO,
                                     // but Request() does via RequestOptions.OnBehalfOf.

Surface

Namespace Purpose
c.Products Products + variants
c.Warehouses Stock locations
c.Stock Levels, movements, reservations, adjust
c.Addresses Customer ship-to addresses
c.Shipments Outbound shipments (Biteship)
c.Shipping Couriers, rates, origin config
c.Licenses Digital licenses + public activation/validate
c.Deliveries Digital download links
c.APIKeys HMAC key management
c.AuditLog Cursor-paginated audit trail
c.Billing Merchant subscription to Fulkruma
c.Integrations Per-provider status
c.Stats Dashboard overview counters
c.Webhooks Endpoints + event log (control plane)
c.Admin Platform-admin partner billing
Function Purpose
VerifyWebhook(body, header, secret, opts) Validate Fulkruma-Signature header and parse envelope.
*Error Single error type — branch on .Code and .Status.

HMAC signing

Every request is signed:

Authorization: Fulkruma-HMAC-SHA256 keyId=<id>, scope=*, signature=<hex>
X-Fulkruma-Timestamp: <unix>

Where signature = HMAC-SHA256(secret, "${METHOD}\n${path}\n${ts}\n${sha256(body)}[\n${idempotency_key}]").

The body bytes that get signed are the bytes that get sent — encoding/json.Marshal produces compact output by default, matching the Node SDK's JSON.stringify and the Python SDK's json.dumps(separators=(',',':')).

Docs

License

MIT

Documentation

Overview

Package fulkruma is the official Go SDK for Fulkruma — stock, warehouses, shipping (Biteship), licenses, deliveries, API keys, audit log, billing, integrations status, stats, webhooks. Mirrors `@forjio/fulkruma-node` and `fulkruma` (Python) 1:1.

All API calls are HMAC-signed:

Authorization: Fulkruma-HMAC-SHA256 keyId=…, scope=*, signature=…
X-Fulkruma-Timestamp: <unix>
X-Fulkruma-On-Behalf-Of: <accountId>   (platform-admin only, optional)
Idempotency-Key: idem_<uuid>           (on replay-sensitive mutations)

The signature payload is `${METHOD}\n${path}\n${ts}\n${sha256(body)}` — plus `\n${idempotencyKey}` when present.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type APIKeyCreateInput

type APIKeyCreateInput struct {
	Description string `json:"description,omitempty"`
	Scope       string `json:"scope,omitempty"`
}

APIKeyCreateInput — POST /api/v1/api-keys body.

type APIKeysResource

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

APIKeysResource — /api/v1/api-keys.

func (*APIKeysResource) Create

func (r *APIKeysResource) Create(ctx context.Context, in APIKeyCreateInput) (map[string]any, error)

Create — POST /api/v1/api-keys (idempotent).

func (*APIKeysResource) List

func (r *APIKeysResource) List(ctx context.Context) ([]map[string]any, error)

List — GET /api/v1/api-keys.

func (*APIKeysResource) Revoke

func (r *APIKeysResource) Revoke(ctx context.Context, id string) (bool, error)

Revoke — POST /api/v1/api-keys/:id/revoke.

type AddressCreateInput

type AddressCreateInput struct {
	CustomerID   string   `json:"customerId"`
	Label        string   `json:"label"`
	ContactName  string   `json:"contactName"`
	ContactPhone string   `json:"contactPhone"`
	Email        string   `json:"email,omitempty"`
	Address      string   `json:"address"`
	PostalCode   string   `json:"postalCode,omitempty"`
	AreaID       string   `json:"areaId,omitempty"`
	Lat          *float64 `json:"lat,omitempty"`
	Lng          *float64 `json:"lng,omitempty"`
	IsDefault    *bool    `json:"isDefault,omitempty"`
}

AddressCreateInput — POST /api/v1/addresses body.

type AddressesResource

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

AddressesResource — /api/v1/addresses.

func (*AddressesResource) Create

Create — POST /api/v1/addresses.

func (*AddressesResource) Delete

func (r *AddressesResource) Delete(ctx context.Context, id string) (bool, error)

Delete — DELETE /api/v1/addresses/:id.

func (*AddressesResource) List

func (r *AddressesResource) List(ctx context.Context, customerID string) ([]CustomerAddress, error)

List — GET /api/v1/addresses.

type AdminResource

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

AdminResource — /api/v1/admin/* (platform-admin scope only).

func (*AdminResource) GetWorkspace

func (r *AdminResource) GetWorkspace(ctx context.Context, accountID string) (*PartnerWorkspace, error)

GetWorkspace — GET /api/v1/admin/workspaces/:accountId.

func (*AdminResource) PartnerUsage

PartnerUsage — GET /api/v1/admin/partner/usage.

func (*AdminResource) ProvisionWorkspace

func (r *AdminResource) ProvisionWorkspace(ctx context.Context, in ProvisionWorkspaceInput) (*PartnerWorkspace, error)

ProvisionWorkspace — POST /api/v1/admin/workspaces. Idempotency key is derived deterministically (`ws_<accountId>_<partner>`), matching the Node SDK so the same input always yields the same key.

type ApiEnvelope

type ApiEnvelope struct {
	Data  json.RawMessage `json:"data"`
	Error *ErrorBody      `json:"error"`
	Meta  *EnvelopeMeta   `json:"meta,omitempty"`
}

ApiEnvelope mirrors the backend's standard `{ data, error, meta }` JSON shape. Generic-free on purpose — every resource method unmarshals `Data` into a concrete struct.

type ArchivedEnvelope

type ArchivedEnvelope struct {
	Archived bool `json:"archived"`
}

ArchivedEnvelope wraps `{ archived: bool }` responses.

type AuditLogListParams

type AuditLogListParams struct {
	Limit     int
	Cursor    string
	Since     string
	EventType string
}

AuditLogListParams — GET /audit-log query.

type AuditLogListResult

type AuditLogListResult struct {
	Entries    []map[string]any `json:"entries"`
	NextCursor string           `json:"nextCursor,omitempty"`
}

AuditLogListResult mirrors the cursor-paginated envelope.

type AuditLogResource

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

AuditLogResource — /api/v1/audit-log.

func (*AuditLogResource) List

List — GET /api/v1/audit-log.

type BillingCheckoutInput

type BillingCheckoutInput struct {
	PlanID     string `json:"planId"`
	SuccessURL string `json:"successUrl,omitempty"`
	CancelURL  string `json:"cancelUrl,omitempty"`
}

BillingCheckoutInput — POST /billing/checkout body.

type BillingCheckoutResult

type BillingCheckoutResult struct {
	URL       string `json:"url"`
	SessionID string `json:"sessionId"`
}

BillingCheckoutResult mirrors `{ url, sessionId }`.

type BillingInvoicesParams

type BillingInvoicesParams struct {
	Limit  int
	Cursor string
}

BillingInvoicesParams — GET /billing/invoices query.

type BillingInvoicesResult

type BillingInvoicesResult struct {
	Invoices   []map[string]any `json:"invoices"`
	NextCursor string           `json:"nextCursor,omitempty"`
}

BillingInvoicesResult mirrors the paginated envelope.

type BillingResource

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

BillingResource — /api/v1/billing/*.

func (*BillingResource) Cancel

func (r *BillingResource) Cancel(ctx context.Context) (map[string]any, error)

Cancel — POST /billing/cancel.

func (*BillingResource) Checkout

Checkout — POST /billing/checkout.

func (*BillingResource) CurrentPlan

func (r *BillingResource) CurrentPlan(ctx context.Context) (map[string]any, error)

CurrentPlan — GET /billing/plan.

func (*BillingResource) Invoices

Invoices — GET /billing/invoices.

func (*BillingResource) Plans

func (r *BillingResource) Plans(ctx context.Context) ([]map[string]any, error)

Plans — GET /billing/plans (untyped passthrough).

func (*BillingResource) Subscription

func (r *BillingResource) Subscription(ctx context.Context) (map[string]any, error)

Subscription — GET /billing/subscription.

func (*BillingResource) Usage

func (r *BillingResource) Usage(ctx context.Context) (map[string]any, error)

Usage — GET /billing/usage.

type Client

type Client struct {

	// Resource namespaces — match the Node SDK property names.
	Products     *ProductsResource
	Warehouses   *WarehousesResource
	Stock        *StockResource
	Addresses    *AddressesResource
	Shipments    *ShipmentsResource
	Shipping     *ShippingResource
	Licenses     *LicensesResource
	Deliveries   *DeliveriesResource
	APIKeys      *APIKeysResource
	AuditLog     *AuditLogResource
	Billing      *BillingResource
	Integrations *IntegrationsResource
	Stats        *StatsResource
	Webhooks     *WebhooksResource
	Admin        *AdminResource
	// contains filtered or unexported fields
}

Client is the HMAC-signed Fulkruma REST client. Construct one per merchant (or one platform-admin client and call ForMerchant per call site).

func NewClient

func NewClient(opts ClientOptions) (*Client, error)

NewClient constructs a Fulkruma client. Returns *Error when required fields are missing — never panics.

func (*Client) BaseURL

func (c *Client) BaseURL() string

BaseURL returns the resolved base URL.

func (*Client) ForMerchant

func (c *Client) ForMerchant(accountId string) *Client

ForMerchant returns a shallow clone scoped to a specific merchant. Only useful with platform-admin keys.

func (*Client) KeyID

func (c *Client) KeyID() string

KeyID returns the HMAC access key id (for logging / dev tooling).

func (*Client) Request

func (c *Client) Request(ctx context.Context, method, path string, body any, out any, opts *RequestOptions) error

Request issues an HMAC-signed request and unmarshals `envelope.data` into `out`. Pass nil out to discard the response body.

`body` is JSON-marshaled (compact, the Go default) before signing. The raw bytes that get signed are exactly the bytes that get sent over the wire — same invariant the backend enforces.

type ClientOptions

type ClientOptions struct {
	// KeyID — HMAC access key id, e.g. "AKIAFULK…". Required.
	KeyID string
	// Secret — HMAC secret. Required.
	Secret string
	// BaseURL — defaults to https://fulkruma.com (or $FULKRUMA_BASE_URL).
	BaseURL string
	// OnBehalfOf — merchant accountId forwarded as the
	// `X-Fulkruma-On-Behalf-Of` header. Platform-admin scope only.
	OnBehalfOf string
	// Timeout — per-request HTTP timeout. Default 30s.
	Timeout time.Duration
	// HTTP — inject a pre-configured client (custom transport, retries).
	// When nil the SDK constructs a fresh http.Client with Timeout.
	HTTP *http.Client
	// Now — clock source for signature timestamps; nil means time.Now.
	// Useful for tests that re-derive the signature deterministically.
	Now func() time.Time
}

ClientOptions controls FulkrumaClient construction. Empty fields fall back to the matching FULKRUMA_* env vars (KEY_ID, SECRET, BASE_URL, ON_BEHALF_OF) — keeping parity with the Node + Python SDKs.

type CustomerAddress

type CustomerAddress struct {
	ID                 string   `json:"id"`
	CustomerID         string   `json:"customerId"`
	AccountID          string   `json:"accountId"`
	Label              string   `json:"label"`
	ContactName        string   `json:"contactName"`
	ContactPhone       string   `json:"contactPhone"`
	Email              *string  `json:"email"`
	Address            string   `json:"address"`
	PostalCode         *string  `json:"postalCode"`
	AreaID             *string  `json:"areaId"`
	Lat                *float64 `json:"lat"`
	Lng                *float64 `json:"lng"`
	BiteshipLocationID *string  `json:"biteshipLocationId"`
	IsDefault          bool     `json:"isDefault"`
	CreatedAt          string   `json:"createdAt"`
	UpdatedAt          string   `json:"updatedAt"`
}

type DeletedEnvelope

type DeletedEnvelope struct {
	Deleted bool `json:"deleted"`
}

DeletedEnvelope wraps `{ deleted: bool }` responses.

type DeliveriesResource

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

DeliveriesResource — /api/v1/deliveries.

func (*DeliveriesResource) Create

Create — POST /api/v1/deliveries (idempotent).

func (*DeliveriesResource) Get

func (r *DeliveriesResource) Get(ctx context.Context, id string) (*Delivery, error)

Get — GET /api/v1/deliveries/:id.

func (*DeliveriesResource) List

func (r *DeliveriesResource) List(ctx context.Context) ([]Delivery, error)

List — GET /api/v1/deliveries.

type Delivery

type Delivery struct {
	ID                string  `json:"id"`
	AccountID         string  `json:"accountId"`
	ProductID         string  `json:"productId"`
	CustomerID        string  `json:"customerId"`
	CheckoutSessionID string  `json:"checkoutSessionId"`
	DownloadCount     int64   `json:"downloadCount"`
	MaxDownloads      int64   `json:"maxDownloads"`
	ExpiresAt         string  `json:"expiresAt"`
	ExternalSource    *string `json:"externalSource"`
	ExternalRef       *string `json:"externalRef"`
	CreatedAt         string  `json:"createdAt"`
	UpdatedAt         string  `json:"updatedAt"`
}

type DeliveryCreateInput

type DeliveryCreateInput struct {
	ProductID         string `json:"productId"`
	CustomerID        string `json:"customerId"`
	CheckoutSessionID string `json:"checkoutSessionId"`
	MaxDownloads      *int64 `json:"maxDownloads,omitempty"`
	ExpiresAt         string `json:"expiresAt,omitempty"`
	ExternalSource    string `json:"externalSource,omitempty"`
	ExternalRef       string `json:"externalRef,omitempty"`
}

DeliveryCreateInput — POST /api/v1/deliveries body.

type EnvelopeMeta

type EnvelopeMeta struct {
	RequestID string `json:"requestId,omitempty"`
	Timestamp string `json:"timestamp,omitempty"`
	Cursor    string `json:"cursor,omitempty"`
	HasMore   bool   `json:"hasMore,omitempty"`
}

EnvelopeMeta carries the pagination + tracing fields the backend returns alongside every response.

type Error

type Error struct {
	Status    int
	Code      string
	Message   string
	RequestID string
}

Error is the single error type returned by every operation in this package. Mirrors the Node SDK's FulkrumaError: an HTTP status (0 for transport-level failures), an opaque code, a human message, and the optional upstream request id from the {meta:{requestId}} envelope.

func (*Error) Error

func (e *Error) Error() string

type ErrorBody

type ErrorBody struct {
	Code    string `json:"code"`
	Message string `json:"message"`
}

ErrorBody is the inner shape of `envelope.error`.

type IntegrationsResource

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

IntegrationsResource — /api/v1/integrations.

func (*IntegrationsResource) Status

Status — GET /api/v1/integrations/status.

type IntegrationsStatus

type IntegrationsStatus struct {
	Huudis     map[string]any `json:"huudis,omitempty"`
	Biteship   map[string]any `json:"biteship,omitempty"`
	Plugipay   map[string]any `json:"plugipay,omitempty"`
	Storlaunch map[string]any `json:"storlaunch,omitempty"`
}

IntegrationsStatus mirrors the per-provider status envelope.

type License

type License struct {
	ID             string        `json:"id"`
	AccountID      string        `json:"accountId"`
	ProductID      string        `json:"productId"`
	CustomerID     string        `json:"customerId"`
	Key            string        `json:"key"`
	Status         LicenseStatus `json:"status"`
	Activations    int64         `json:"activations"`
	MaxActivations int64         `json:"maxActivations"`
	ExpiresAt      *string       `json:"expiresAt"`
	ExternalSource *string       `json:"externalSource"`
	ExternalRef    *string       `json:"externalRef"`
	CreatedAt      string        `json:"createdAt"`
	UpdatedAt      string        `json:"updatedAt"`
}

type LicenseActivateInput

type LicenseActivateInput struct {
	Key        string `json:"key"`
	InstanceID string `json:"instanceId"`
}

LicenseActivateInput / LicenseDeactivateInput are public-unauthenticated.

type LicenseActivateResult

type LicenseActivateResult struct {
	License       License           `json:"license"`
	Activation    LicenseActivation `json:"activation"`
	AlreadyActive bool              `json:"alreadyActive"`
}

LicenseActivateResult mirrors the Node response.

type LicenseActivation

type LicenseActivation struct {
	ID            string  `json:"id"`
	LicenseID     string  `json:"licenseId"`
	InstanceID    string  `json:"instanceId"`
	ActivatedAt   string  `json:"activatedAt"`
	DeactivatedAt *string `json:"deactivatedAt"`
}

type LicenseDeactivateResult

type LicenseDeactivateResult struct {
	Deactivated        bool  `json:"deactivated"`
	AlreadyDeactivated bool  `json:"alreadyDeactivated"`
	Activations        int64 `json:"activations"`
}

LicenseDeactivateResult mirrors the Node response.

type LicenseIssueInput

type LicenseIssueInput struct {
	ProductID      string `json:"productId"`
	CustomerID     string `json:"customerId"`
	MaxActivations *int64 `json:"maxActivations,omitempty"`
	ExpiresAt      string `json:"expiresAt,omitempty"`
	ExternalSource string `json:"externalSource,omitempty"`
	ExternalRef    string `json:"externalRef,omitempty"`
}

LicenseIssueInput — POST /api/v1/licenses body.

type LicenseStatus

type LicenseStatus string

LicenseStatus — "active" | "revoked".

const (
	LicenseActive  LicenseStatus = "active"
	LicenseRevoked LicenseStatus = "revoked"
)

type LicenseValidateParams

type LicenseValidateParams struct {
	Key       string
	ProductID string
}

LicenseValidateParams — GET /licenses/validate query.

type LicenseValidateResult

type LicenseValidateResult struct {
	Valid          bool    `json:"valid"`
	Key            string  `json:"key"`
	Status         *string `json:"status"`
	ProductID      *string `json:"productId"`
	Activations    *int64  `json:"activations"`
	MaxActivations *int64  `json:"maxActivations"`
	ExpiresAt      *string `json:"expiresAt"`
}

LicenseValidateResult — payload returned by GET /licenses/validate.

type LicensesResource

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

LicensesResource — /api/v1/licenses.

func (*LicensesResource) Activate

Activate — public unauthenticated. Pass arbitrary creds; the HMAC is still attached but the backend treats this route as unauthenticated.

func (*LicensesResource) Deactivate

Deactivate — public unauthenticated.

func (*LicensesResource) Issue

Issue — POST /api/v1/licenses (idempotent).

func (*LicensesResource) List

func (r *LicensesResource) List(ctx context.Context) ([]License, error)

List — GET /api/v1/licenses.

func (*LicensesResource) Revoke

func (r *LicensesResource) Revoke(ctx context.Context, id string) (*License, error)

Revoke — POST /api/v1/licenses/:id/revoke.

func (*LicensesResource) Validate

Validate — public unauthenticated.

type PartnerUsageMerchant

type PartnerUsageMerchant struct {
	AccountID       string `json:"accountId"`
	Shipments       int64  `json:"shipments"`
	LicensesIssued  int64  `json:"licensesIssued"`
	Deliveries      int64  `json:"deliveries"`
	ChargeableCents int64  `json:"chargeableCents"`
}

type PartnerUsageParams

type PartnerUsageParams struct {
	Partner string
	From    string
	To      string
}

PartnerUsageParams — GET /admin/partner/usage query.

type PartnerUsagePeriod

type PartnerUsagePeriod struct {
	From string `json:"from"`
	To   string `json:"to"`
}

type PartnerUsageSummary

type PartnerUsageSummary struct {
	Partner    string                 `json:"partner"`
	Period     PartnerUsagePeriod     `json:"period"`
	Totals     PartnerUsageTotals     `json:"totals"`
	ByMerchant []PartnerUsageMerchant `json:"byMerchant"`
}

type PartnerUsageTotals

type PartnerUsageTotals struct {
	Shipments       int64 `json:"shipments"`
	LicensesIssued  int64 `json:"licensesIssued"`
	Deliveries      int64 `json:"deliveries"`
	ChargeableCents int64 `json:"chargeableCents"`
}

type PartnerWorkspace

type PartnerWorkspace struct {
	AccountID     string  `json:"accountId"`
	Partner       string  `json:"partner"`
	DiscountRate  float64 `json:"discountRate"`
	BrandName     *string `json:"brandName"`
	BusinessEmail *string `json:"businessEmail"`
	CreatedAt     string  `json:"createdAt"`
}

type Product

type Product struct {
	ID             string           `json:"id"`
	AccountID      string           `json:"accountId"`
	Name           string           `json:"name"`
	SKU            *string          `json:"sku"`
	Description    *string          `json:"description"`
	Type           ProductType      `json:"type"`
	Weight         *float64         `json:"weight"`
	Length         *float64         `json:"length"`
	Width          *float64         `json:"width"`
	Height         *float64         `json:"height"`
	LicenseEnabled bool             `json:"licenseEnabled"`
	MaxActivations int64            `json:"maxActivations"`
	ExternalRef    *string          `json:"externalRef"`
	ExternalSource *string          `json:"externalSource"`
	Archived       bool             `json:"archived"`
	Metadata       map[string]any   `json:"metadata"`
	CreatedAt      string           `json:"createdAt"`
	UpdatedAt      string           `json:"updatedAt"`
	Variants       []ProductVariant `json:"variants,omitempty"`
}

Product matches the Node SDK type 1:1.

type ProductCreateInput

type ProductCreateInput struct {
	Name           string      `json:"name"`
	SKU            string      `json:"sku,omitempty"`
	Description    string      `json:"description,omitempty"`
	Type           ProductType `json:"type,omitempty"`
	Weight         *float64    `json:"weight,omitempty"`
	Length         *float64    `json:"length,omitempty"`
	Width          *float64    `json:"width,omitempty"`
	Height         *float64    `json:"height,omitempty"`
	LicenseEnabled *bool       `json:"licenseEnabled,omitempty"`
	MaxActivations *int64      `json:"maxActivations,omitempty"`
	ExternalRef    string      `json:"externalRef,omitempty"`
	ExternalSource string      `json:"externalSource,omitempty"`
}

ProductCreateInput is the request body for Products.Create. Optional fields are JSON-omitempty so zero values don't override backend defaults.

type ProductEnvelope

type ProductEnvelope struct {
	Product Product `json:"product"`
}

ProductEnvelope wraps `{ product: Product }` responses.

type ProductListEnvelope

type ProductListEnvelope struct {
	Products []Product `json:"products"`
}

ProductListEnvelope wraps `{ products: [] }` responses.

type ProductListParams

type ProductListParams struct {
	Archived *bool
}

ProductListParams filters the list endpoint.

type ProductType

type ProductType string

ProductType — "physical" | "digital" | "license".

const (
	ProductTypePhysical ProductType = "physical"
	ProductTypeDigital  ProductType = "digital"
	ProductTypeLicense  ProductType = "license"
)

type ProductUpdateInput

type ProductUpdateInput struct {
	Name           *string      `json:"name,omitempty"`
	SKU            *string      `json:"sku,omitempty"`
	Description    *string      `json:"description,omitempty"`
	Type           *ProductType `json:"type,omitempty"`
	Weight         *float64     `json:"weight,omitempty"`
	Length         *float64     `json:"length,omitempty"`
	Width          *float64     `json:"width,omitempty"`
	Height         *float64     `json:"height,omitempty"`
	LicenseEnabled *bool        `json:"licenseEnabled,omitempty"`
	MaxActivations *int64       `json:"maxActivations,omitempty"`
	ExternalRef    *string      `json:"externalRef,omitempty"`
	ExternalSource *string      `json:"externalSource,omitempty"`
}

ProductUpdateInput patches a product — every field is optional.

type ProductVariant

type ProductVariant struct {
	ID                string   `json:"id"`
	ProductID         string   `json:"productId"`
	SKU               *string  `json:"sku"`
	Name              string   `json:"name"`
	PriceCents        int64    `json:"priceCents"`
	CostCents         *int64   `json:"costCents"`
	LowStockThreshold *int64   `json:"lowStockThreshold"`
	Weight            *float64 `json:"weight"`
	IsDefault         bool     `json:"isDefault"`
	Archived          bool     `json:"archived"`
	ExternalRef       *string  `json:"externalRef"`
	ExternalSource    *string  `json:"externalSource"`
	CreatedAt         string   `json:"createdAt"`
	UpdatedAt         string   `json:"updatedAt"`
}

ProductVariant matches the Node SDK type 1:1.

type ProductsResource

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

ProductsResource groups every /api/v1/products call.

func (*ProductsResource) AddVariant

func (r *ProductsResource) AddVariant(ctx context.Context, productID string, in VariantCreateInput) (*ProductVariant, error)

AddVariant — POST /api/v1/products/:id/variants.

func (*ProductsResource) Archive

func (r *ProductsResource) Archive(ctx context.Context, id string) (bool, error)

Archive — DELETE /api/v1/products/:id (soft delete).

func (*ProductsResource) ArchiveVariant

func (r *ProductsResource) ArchiveVariant(ctx context.Context, productID, variantID string) (bool, error)

ArchiveVariant — DELETE /api/v1/products/:productId/variants/:variantId.

func (*ProductsResource) Create

Create — POST /api/v1/products (idempotent via auto-generated key).

func (*ProductsResource) Get

func (r *ProductsResource) Get(ctx context.Context, id string) (*Product, error)

Get — GET /api/v1/products/:id.

func (*ProductsResource) List

List — GET /api/v1/products.

func (*ProductsResource) Update

func (r *ProductsResource) Update(ctx context.Context, id string, patch ProductUpdateInput) (*Product, error)

Update — PATCH /api/v1/products/:id.

func (*ProductsResource) UpdateVariant

func (r *ProductsResource) UpdateVariant(ctx context.Context, productID, variantID string, patch VariantUpdateInput) (*ProductVariant, error)

UpdateVariant — PATCH /api/v1/products/:productId/variants/:variantId.

type ProvisionWorkspaceInput

type ProvisionWorkspaceInput struct {
	AccountID     string  `json:"accountId"`
	Partner       string  `json:"partner"`
	DiscountRate  float64 `json:"discountRate"`
	BrandName     string  `json:"brandName,omitempty"`
	BusinessEmail string  `json:"businessEmail,omitempty"`
}

ProvisionWorkspaceInput — POST /admin/workspaces body.

type RequestOptions

type RequestOptions struct {
	IdempotencyKey string
	OnBehalfOf     string // per-call override; "" falls back to default
}

RequestOptions tunes a single low-level Request call. Most callers use the typed resource methods instead.

type RevokedEnvelope

type RevokedEnvelope struct {
	Revoked bool `json:"revoked"`
}

RevokedEnvelope wraps `{ revoked: bool }`.

type Shipment

type Shipment struct {
	ID                  string           `json:"id"`
	AccountID           string           `json:"accountId"`
	ProductID           *string          `json:"productId"`
	CheckoutSessionID   *string          `json:"checkoutSessionId"`
	CustomerID          *string          `json:"customerId"`
	CustomerEmail       *string          `json:"customerEmail"`
	BiteshipOrderID     string           `json:"biteshipOrderId"`
	BiteshipTrackingID  *string          `json:"biteshipTrackingId"`
	WaybillID           *string          `json:"waybillId"`
	CourierCode         string           `json:"courierCode"`
	CourierServiceCode  string           `json:"courierServiceCode"`
	CourierType         string           `json:"courierType"`
	Status              ShipmentStatus   `json:"status"`
	TrackingURL         *string          `json:"trackingUrl"`
	LabelURL            *string          `json:"labelUrl"`
	Price               float64          `json:"price"`
	Insurance           float64          `json:"insurance"`
	Insured             bool             `json:"insured"`
	OriginSnapshot      map[string]any   `json:"originSnapshot"`
	DestinationSnapshot map[string]any   `json:"destinationSnapshot"`
	Items               []map[string]any `json:"items"`
	CancelReason        *string          `json:"cancelReason"`
	ExternalSource      *string          `json:"externalSource"`
	ExternalRef         *string          `json:"externalRef"`
	CreatedAt           string           `json:"createdAt"`
	UpdatedAt           string           `json:"updatedAt"`
}

type ShipmentCreateInput

type ShipmentCreateInput struct {
	ProductID          string           `json:"productId,omitempty"`
	CheckoutSessionID  string           `json:"checkoutSessionId,omitempty"`
	CustomerID         string           `json:"customerId,omitempty"`
	CustomerEmail      string           `json:"customerEmail,omitempty"`
	CourierCode        string           `json:"courierCode"`
	CourierServiceCode string           `json:"courierServiceCode"`
	CourierType        string           `json:"courierType"`
	Price              float64          `json:"price"`
	Insurance          *float64         `json:"insurance,omitempty"`
	Insured            *bool            `json:"insured,omitempty"`
	Origin             map[string]any   `json:"origin"`
	Destination        map[string]any   `json:"destination"`
	Items              []map[string]any `json:"items"`
	ExternalSource     string           `json:"externalSource,omitempty"`
	ExternalRef        string           `json:"externalRef,omitempty"`
}

ShipmentCreateInput is intentionally loosely typed to match the Node SDK — origin/destination/items are arbitrary JSON shapes the backend validates.

type ShipmentStatus

type ShipmentStatus string

ShipmentStatus — enumerated.

const (
	ShipmentPending     ShipmentStatus = "pending"
	ShipmentConfirmed   ShipmentStatus = "confirmed"
	ShipmentAllocated   ShipmentStatus = "allocated"
	ShipmentPickingUp   ShipmentStatus = "picking_up"
	ShipmentPickedUp    ShipmentStatus = "picked_up"
	ShipmentDroppingOff ShipmentStatus = "dropping_off"
	ShipmentInTransit   ShipmentStatus = "in_transit"
	ShipmentDelivered   ShipmentStatus = "delivered"
	ShipmentCancelled   ShipmentStatus = "cancelled"
	ShipmentReturned    ShipmentStatus = "returned"
	ShipmentFailed      ShipmentStatus = "failed"
)

type ShipmentsResource

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

ShipmentsResource — /api/v1/shipments.

func (*ShipmentsResource) Create

Create — POST /api/v1/shipments (idempotent).

func (*ShipmentsResource) Get

func (r *ShipmentsResource) Get(ctx context.Context, id string) (*Shipment, error)

Get — GET /api/v1/shipments/:id.

func (*ShipmentsResource) List

func (r *ShipmentsResource) List(ctx context.Context, status string) ([]Shipment, error)

List — GET /api/v1/shipments.

type ShippingRatesInput

type ShippingRatesInput struct {
	Destination map[string]any   `json:"destination"`
	Items       []map[string]any `json:"items"`
	Insurance   *bool            `json:"insurance,omitempty"`
}

ShippingRatesInput — POST /api/v1/shipping/rates body.

type ShippingResource

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

ShippingResource — /api/v1/shipping/*.

func (*ShippingResource) Couriers

func (r *ShippingResource) Couriers(ctx context.Context) ([]map[string]any, error)

Couriers — GET /api/v1/shipping/couriers (untyped — passthrough to backend).

func (*ShippingResource) Origin

func (r *ShippingResource) Origin(ctx context.Context) (map[string]any, error)

Origin — GET /api/v1/shipping/origin.

func (*ShippingResource) Rates

Rates — POST /api/v1/shipping/rates.

func (*ShippingResource) SetOrigin

func (r *ShippingResource) SetOrigin(ctx context.Context, in map[string]any) (map[string]any, error)

SetOrigin — PATCH /api/v1/shipping/origin.

type StatsOverview

type StatsOverview struct {
	Counters map[string]int64 `json:"counters"`
	Recent   map[string]any   `json:"recent"`
}

StatsOverview mirrors the overview envelope.

type StatsResource

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

StatsResource — /api/v1/stats.

func (*StatsResource) Overview

func (r *StatsResource) Overview(ctx context.Context) (*StatsOverview, error)

Overview — GET /api/v1/stats/overview.

type StockAdjustInput

type StockAdjustInput struct {
	VariantID   string              `json:"variantId"`
	WarehouseID string              `json:"warehouseId"`
	Delta       int64               `json:"delta"`
	Reason      StockMovementReason `json:"reason"`
	Note        string              `json:"note,omitempty"`
}

StockAdjustInput — POST /api/v1/stock/adjust body.

type StockAdjustResult

type StockAdjustResult struct {
	Stock    VariantStock  `json:"stock"`
	Movement StockMovement `json:"movement"`
}

StockAdjustResult mirrors `{ stock, movement }`.

type StockMovement

type StockMovement struct {
	ID          string              `json:"id"`
	VariantID   string              `json:"variantId"`
	WarehouseID string              `json:"warehouseId"`
	Delta       int64               `json:"delta"`
	Reason      StockMovementReason `json:"reason"`
	ReferenceID *string             `json:"referenceId"`
	Note        *string             `json:"note"`
	CreatedAt   string              `json:"createdAt"`
	CreatedBy   *string             `json:"createdBy"`
}

type StockMovementReason

type StockMovementReason string

StockMovementReason — enumerated values match Node SDK.

const (
	StockMovementManualAdjust       StockMovementReason = "manual_adjust"
	StockMovementCheckoutReserve    StockMovementReason = "checkout_reserve"
	StockMovementCheckoutCommit     StockMovementReason = "checkout_commit"
	StockMovementCheckoutRelease    StockMovementReason = "checkout_release"
	StockMovementRefundRestock      StockMovementReason = "refund_restock"
	StockMovementTransferIn         StockMovementReason = "transfer_in"
	StockMovementTransferOut        StockMovementReason = "transfer_out"
	StockMovementDamaged            StockMovementReason = "damaged"
	StockMovementReturnedToSupplier StockMovementReason = "returned_to_supplier"
	StockMovementInitialStock       StockMovementReason = "initial_stock"
	StockMovementImport             StockMovementReason = "import"
)

type StockReservation

type StockReservation struct {
	ID                string  `json:"id"`
	VariantID         string  `json:"variantId"`
	WarehouseID       string  `json:"warehouseId"`
	Quantity          int64   `json:"quantity"`
	CheckoutSessionID string  `json:"checkoutSessionId"`
	ExpiresAt         string  `json:"expiresAt"`
	ConsumedAt        *string `json:"consumedAt"`
	ReleasedAt        *string `json:"releasedAt"`
	CreatedAt         string  `json:"createdAt"`
}

type StockResource

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

StockResource — /api/v1/stock.

func (*StockResource) Adjust

Adjust — POST /api/v1/stock/adjust (idempotent).

func (*StockResource) Levels

func (r *StockResource) Levels(ctx context.Context, variantID string) ([]VariantStock, error)

Levels — GET /api/v1/stock/levels.

func (*StockResource) Movements

func (r *StockResource) Movements(ctx context.Context, variantID string) ([]StockMovement, error)

Movements — GET /api/v1/stock/movements.

func (*StockResource) Reservations

func (r *StockResource) Reservations(ctx context.Context) ([]StockReservation, error)

Reservations — GET /api/v1/stock/reservations.

type VariantCreateInput

type VariantCreateInput struct {
	Name              string   `json:"name"`
	SKU               string   `json:"sku,omitempty"`
	PriceCents        *int64   `json:"priceCents,omitempty"`
	CostCents         *int64   `json:"costCents,omitempty"`
	LowStockThreshold *int64   `json:"lowStockThreshold,omitempty"`
	Weight            *float64 `json:"weight,omitempty"`
	IsDefault         *bool    `json:"isDefault,omitempty"`
	ExternalRef       string   `json:"externalRef,omitempty"`
	ExternalSource    string   `json:"externalSource,omitempty"`
}

VariantCreateInput is the request body for Products.AddVariant.

type VariantEnvelope

type VariantEnvelope struct {
	Variant ProductVariant `json:"variant"`
}

VariantEnvelope wraps `{ variant: ProductVariant }` responses.

type VariantStock

type VariantStock struct {
	ID          string                    `json:"id"`
	VariantID   string                    `json:"variantId"`
	WarehouseID string                    `json:"warehouseId"`
	Quantity    int64                     `json:"quantity"`
	UpdatedAt   string                    `json:"updatedAt"`
	Warehouse   *VariantStockWarehouseRef `json:"warehouse,omitempty"`
}

type VariantStockWarehouseRef

type VariantStockWarehouseRef struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

VariantStockWarehouseRef is the truncated warehouse hint the backend inlines on level responses.

type VariantUpdateInput

type VariantUpdateInput struct {
	Name              *string  `json:"name,omitempty"`
	SKU               *string  `json:"sku,omitempty"`
	PriceCents        *int64   `json:"priceCents,omitempty"`
	CostCents         *int64   `json:"costCents,omitempty"`
	LowStockThreshold *int64   `json:"lowStockThreshold,omitempty"`
	Weight            *float64 `json:"weight,omitempty"`
	IsDefault         *bool    `json:"isDefault,omitempty"`
	ExternalRef       *string  `json:"externalRef,omitempty"`
	ExternalSource    *string  `json:"externalSource,omitempty"`
}

VariantUpdateInput is the PATCH-only counterpart.

type VerifyWebhookOptions

type VerifyWebhookOptions struct {
	// ToleranceSec — max drift in seconds between the signature timestamp
	// and the current clock. Default 300 (5 min). Zero or negative means
	// "use the default".
	ToleranceSec int64
	// Now — clock for the freshness check. Nil means time.Now. Useful in
	// tests.
	Now func() time.Time
}

VerifyWebhookOptions tunes VerifyWebhook. A nil pointer uses defaults.

type Warehouse

type Warehouse struct {
	ID        string  `json:"id"`
	AccountID string  `json:"accountId"`
	Name      string  `json:"name"`
	Address   *string `json:"address"`
	City      *string `json:"city"`
	Postal    *string `json:"postal"`
	Phone     *string `json:"phone"`
	IsDefault bool    `json:"isDefault"`
	Archived  bool    `json:"archived"`
	CreatedAt string  `json:"createdAt"`
	UpdatedAt string  `json:"updatedAt"`
}

type WarehouseCreateInput

type WarehouseCreateInput struct {
	Name      string   `json:"name"`
	Address   string   `json:"address,omitempty"`
	City      string   `json:"city,omitempty"`
	Postal    string   `json:"postal,omitempty"`
	Lat       *float64 `json:"lat,omitempty"`
	Lng       *float64 `json:"lng,omitempty"`
	Phone     string   `json:"phone,omitempty"`
	IsDefault *bool    `json:"isDefault,omitempty"`
}

WarehouseCreateInput — POST /api/v1/warehouses body.

type WarehouseUpdateInput

type WarehouseUpdateInput struct {
	Name      *string  `json:"name,omitempty"`
	Address   *string  `json:"address,omitempty"`
	City      *string  `json:"city,omitempty"`
	Postal    *string  `json:"postal,omitempty"`
	Lat       *float64 `json:"lat,omitempty"`
	Lng       *float64 `json:"lng,omitempty"`
	Phone     *string  `json:"phone,omitempty"`
	IsDefault *bool    `json:"isDefault,omitempty"`
}

WarehouseUpdateInput — PATCH-only counterpart.

type WarehousesResource

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

WarehousesResource — /api/v1/warehouses.

func (*WarehousesResource) Archive

func (r *WarehousesResource) Archive(ctx context.Context, id string) (bool, error)

Archive — DELETE /api/v1/warehouses/:id.

func (*WarehousesResource) Create

Create — POST /api/v1/warehouses.

func (*WarehousesResource) List

func (r *WarehousesResource) List(ctx context.Context) ([]Warehouse, error)

List — GET /api/v1/warehouses.

func (*WarehousesResource) Update

Update — PATCH /api/v1/warehouses/:id.

type WebhookEndpointCreateInput

type WebhookEndpointCreateInput struct {
	URL         string   `json:"url"`
	Events      []string `json:"events,omitempty"`
	Description string   `json:"description,omitempty"`
}

WebhookEndpointCreateInput — POST /webhooks/endpoints body.

type WebhookEndpointUpdateInput

type WebhookEndpointUpdateInput struct {
	URL         *string   `json:"url,omitempty"`
	Events      *[]string `json:"events,omitempty"`
	Description *string   `json:"description,omitempty"`
	Active      *bool     `json:"active,omitempty"`
}

WebhookEndpointUpdateInput — PATCH body. Fields are pointers to distinguish "leave alone" from "set to zero".

type WebhookEventEnvelope

type WebhookEventEnvelope struct {
	ID         string          `json:"id"`
	Type       string          `json:"type"`
	OccurredAt string          `json:"occurredAt"`
	AccountID  *string         `json:"accountId"`
	Data       json.RawMessage `json:"data"`
	Metadata   map[string]any  `json:"metadata"`
}

WebhookEventEnvelope is the parsed shape returned by VerifyWebhook. Data stays as RawMessage so callers can switch on Type before unmarshaling.

func VerifyWebhook

func VerifyWebhook(rawBody []byte, signatureHeader, secret string, opts *VerifyWebhookOptions) (*WebhookEventEnvelope, error)

VerifyWebhook verifies and parses an inbound Fulkruma webhook.

Fulkruma signs every webhook delivery with::

Fulkruma-Signature: t=<unix>,v1=<hex>

where `<hex> = HMAC-SHA256(secret, fmt.Sprintf("%s.%s", t, rawBody))`. The raw body bytes are what got signed — re-serialising parsed JSON will drift whitespace and the signature will never match.

Returns the parsed envelope on success. Returns *Error on any failure (missing header, drift, bad signature, malformed JSON).

net/http example:

func fulkrumaWebhook(w http.ResponseWriter, r *http.Request) {
    body, err := io.ReadAll(r.Body)
    if err != nil { http.Error(w, "bad body", 400); return }
    sig := r.Header.Get("Fulkruma-Signature")
    event, err := fulkruma.VerifyWebhook(body, sig, os.Getenv("FULKRUMA_WEBHOOK_SECRET"), nil)
    if err != nil { http.Error(w, err.Error(), 400); return }
    // switch event.Type { … }; reply 204.
}

type WebhookEventsListParams

type WebhookEventsListParams struct {
	Limit  int
	Cursor string
	Type   string
}

WebhookEventsListParams — GET /webhooks/events query.

type WebhookEventsListResult

type WebhookEventsListResult struct {
	Events     []map[string]any `json:"events"`
	NextCursor string           `json:"nextCursor,omitempty"`
}

WebhookEventsListResult mirrors the cursor-paginated envelope.

type WebhooksResource

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

WebhooksResource — /api/v1/webhooks/*. (For signature verification of inbound deliveries, see VerifyWebhook.)

func (*WebhooksResource) CreateEndpoint

func (r *WebhooksResource) CreateEndpoint(ctx context.Context, in WebhookEndpointCreateInput) (map[string]any, error)

CreateEndpoint — POST /api/v1/webhooks/endpoints (idempotent).

func (*WebhooksResource) DeleteEndpoint

func (r *WebhooksResource) DeleteEndpoint(ctx context.Context, id string) (bool, error)

DeleteEndpoint — DELETE /api/v1/webhooks/endpoints/:id.

func (*WebhooksResource) ListEndpoints

func (r *WebhooksResource) ListEndpoints(ctx context.Context) ([]map[string]any, error)

ListEndpoints — GET /api/v1/webhooks/endpoints.

func (*WebhooksResource) ListEvents

ListEvents — GET /api/v1/webhooks/events.

func (*WebhooksResource) UpdateEndpoint

func (r *WebhooksResource) UpdateEndpoint(ctx context.Context, id string, patch WebhookEndpointUpdateInput) (map[string]any, error)

UpdateEndpoint — PATCH /api/v1/webhooks/endpoints/:id.

Jump to

Keyboard shortcuts

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