nodedata

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Aug 6, 2026 License: MIT Imports: 19 Imported by: 0

README

node-data-go

The official Go client for the Node Data API — the marketplace for robotics models, datasets, and physical-AI workflows.

Zero dependencies beyond the standard library. Go 1.21+.

go get github.com/Node-Data/node-data-go

Quickstart

package main

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

	nodedata "github.com/Node-Data/node-data-go"
)

func main() {
	client, err := nodedata.New(nodedata.Config{APIKey: os.Getenv("NODE_DATA_API_KEY")})
	if err != nil {
		log.Fatal(err)
	}

	ctx := context.Background()

	// Confirm the key works and see what it can do.
	if ping := client.Ping(ctx); ping.OK {
		fmt.Printf("%s (%v) in %s\n", ping.Account, ping.Scopes, ping.Latency)
	}

	page, err := client.Models.List(ctx, &nodedata.ListAssetsParams{Q: "grasping", Limit: 20})
	if err != nil {
		log.Fatal(err)
	}
	for _, asset := range page.Items {
		fmt.Println(asset.Slug, asset.Price.Amount)
	}
}

APIKey falls back to $NODE_DATA_API_KEY, and BaseURL to $NODE_DATA_BASE_URL, so the zero Config works in a configured environment. Create a key at nodedata.ai/dashboard/api-keys.

Configuration

client, err := nodedata.New(nodedata.Config{
	APIKey:     "nd_live_…",
	BaseURL:    "https://www.nodedata.ai/api/v1", // default
	Timeout:    60 * time.Second,                 // default
	MaxRetries: 2,                                // default; use NoRetries to disable
	Headers:    map[string]string{"X-Trace": "abc"},
	HTTPClient: myClient,
	OnRequest: func(info nodedata.RequestInfo) {
		log.Printf("%s %s (attempt %d)", info.Method, info.URL, info.Attempt)
	},
})

Transient failures (429, 5xx, network) retry with full-jitter exponential backoff. A Retry-After header always wins over the computed delay. Any option can be overridden per call:

asset, err := client.Models.Retrieve(ctx, "grasp-net",
	nodedata.WithTimeout(5*time.Second),
	nodedata.WithMaxRetries(0),
)

Resources

Namespace Methods
client.Models List Retrieve Create Update Delete
client.Datasets List Retrieve Create
client.Account Retrieve Usage
client.Payouts Retrieve
client.WebhookEndpoints List Create Retrieve Update Delete Deliveries
client.Inference Models Create Stream StreamToText

Optional request fields are pointers so an explicit zero is distinguishable from "unset" — a free listing is PriceCents: nodedata.Ptr(int64(0)), not an omitted field:

asset, err := client.Models.Create(ctx, nodedata.CreateAssetParams{
	Title:       "Grasp policy",
	Description: "Trained on 40k kitchen clips",
	Type:        "model",
	PriceCents:  nodedata.Ptr(int64(0)), // free
})

Pagination

List returns one page plus the means to walk the rest.

page, err := client.Models.List(ctx, &nodedata.ListAssetsParams{Limit: 100})

// Just this page:
for _, asset := range page.Items { ... }

// Every remaining item, one call per page:
err = page.Each(ctx, func(asset nodedata.Asset) error {
	fmt.Println(asset.Slug)
	return nil // return nodedata.StopIteration to stop early
})

// Or collect them (0 means no limit — beware large collections):
all, err := page.All(ctx, 500)

Errors

Every non-2xx response becomes a typed error carrying the API's stable Code, the HTTP Status, and the x-request-id worth quoting in a support request. Match with errors.As:

_, err := client.Payouts.Retrieve(ctx, 0)

var permErr *nodedata.PermissionError
if errors.As(err, &permErr) {
	log.Fatalf("key needs the %s scope", permErr.RequiredScope())
}

var rateErr *nodedata.RateLimitError
if errors.As(err, &rateErr) && rateErr.RetryAfter != nil {
	time.Sleep(*rateErr.RetryAfter)
}
Type When
*BadRequestError 400
*AuthenticationError 401 — missing, malformed, revoked, or expired key
*PaymentRequiredError 402 — key exists but is not activated
*PermissionError 403 — valid key, missing scope (RequiredScope())
*NotFoundError 404
*ConflictError 409
*RateLimitError 429 — check RetryAfter
*ServerError 5xx
*APIError any of the above; the general case
*TimeoutError request exceeded its timeout
*ConnectionError never reached the server, or cancelled

The specific types unwrap to the general ones, so errors.As(err, &apiErr) matches every HTTP failure and errors.As(err, &connErr) matches timeouts too.

Inference

The wire format is OpenAI-compatible, so any OpenAI client pointed at https://www.nodedata.ai/api/v1 works as well — this namespace just saves a second dependency. Requires a premium key (inference:run).

completion, err := client.Inference.Create(ctx, nodedata.ChatCompletionParams{
	Model:    "nd-fast-1",
	Messages: []nodedata.ChatMessage{{Role: nodedata.RoleUser, Content: "Plan a pick-and-place sequence."}},
})
fmt.Println(completion.Choices[0].Message.Content)

Streaming yields parsed SSE chunks. Streams are never retried mid-flight, so the context is the only cancellation path:

stream, err := client.Inference.Stream(ctx, params)
if err != nil {
	log.Fatal(err)
}
defer stream.Close()

for stream.Next() {
	fmt.Print(stream.Current())
}
if err := stream.Err(); err != nil {
	log.Fatal(err)
}

Or collect the whole thing, observing tokens as they arrive:

text, err := client.Inference.StreamToText(ctx, params, func(token string) {
	fmt.Print(token)
})

Webhooks

Node Data signs every delivery with the endpoint's whsec_… secret and sends the result in the nd-signature header. VerifyRequest reads the raw body itself, so you cannot accidentally verify against re-serialized JSON — which would change key order and whitespace, and fail every time.

func handler(w http.ResponseWriter, r *http.Request) {
	event, err := nodedata.VerifyRequest(r, os.Getenv("ND_WEBHOOK_SECRET"))
	if err != nil {
		http.Error(w, "bad signature", http.StatusBadRequest)
		return
	}

	switch event.Type {
	case nodedata.EventListingPurchased:
		var data struct {
			AmountCents int64 `json:"amount_cents"`
		}
		json.Unmarshal(event.Data, &data)
		log.Printf("sold for %d cents", data.AmountCents)
	}

	w.WriteHeader(http.StatusOK)
}

Verification rejects malformed headers, timestamps outside a 5-minute tolerance (replays), and mismatched HMACs. During a secret rotation the platform may send two signatures; either matching is accepted. Comparison is constant-time.

Escape hatch

For endpoints this SDK does not model yet — same auth, retries, and error mapping:

var result map[string]any
err := client.Do(ctx, "GET", "/some/new/endpoint", nil, &result)

Development

go test ./...          # 43 tests
go test -race ./...
go vet ./...

License

MIT

Documentation

Overview

Package nodedata is the official Go client for the Node Data API — the marketplace for robotics models, datasets, and physical-AI workflows.

Docs: https://nodedata.dev/docs API keys: https://www.nodedata.ai/dashboard/api-keys

Zero dependencies beyond the standard library.

client, err := nodedata.New(nodedata.Config{APIKey: os.Getenv("NODE_DATA_API_KEY")})
if err != nil {
    log.Fatal(err)
}

page, err := client.Models.List(ctx, &nodedata.ListAssetsParams{Q: "grasping", Limit: 20})
if err != nil {
    log.Fatal(err)
}
for _, asset := range page.Items {
    fmt.Println(asset.Slug, asset.Price.Amount)
}

Index

Constants

View Source
const (
	WebhookMalformedSignature      = "malformed_signature"
	WebhookTimestampOutOfTolerance = "timestamp_out_of_tolerance"
	WebhookSignatureMismatch       = "signature_mismatch"
)

Webhook verification failure codes.

View Source
const (
	DefaultTimeout    = 60 * time.Second
	DefaultMaxRetries = 2
)

Defaults applied when Config leaves a field zero.

View Source
const (
	ModeTest    = "test"
	ModeLive    = "live"
	ModeUnknown = "unknown"
)

Key modes.

View Source
const (
	TierFree    = "free"
	TierPremium = "premium"
)

Key tiers.

View Source
const (
	ScopeModelsRead     = "models:read"
	ScopeModelsUpload   = "models:upload"
	ScopeDatasetsRead   = "datasets:read"
	ScopeDatasetsUpload = "datasets:upload"
	ScopeListingsRead   = "listings:read"
	ScopeListingsWrite  = "listings:write"
	ScopePayoutsRead    = "payouts:read"
	ScopeDeployWrite    = "deploy:write"
	ScopeWebhooksWrite  = "webhooks:write"
	ScopeInferenceRun   = "inference:run"
)

Scopes an API key can carry. An empty scope list means unrestricted access.

View Source
const (
	EventListingPurchased   = "listing.purchased"
	EventListingPublished   = "listing.published"
	EventListingUnpublished = "listing.unpublished"
	EventAssetDownloaded    = "asset.downloaded"
	EventFactoryJobComplete = "factory.job.completed"
	EventFactoryJobPublish  = "factory.job.published"
	EventPayoutPaid         = "payout.paid"
)

Webhook event types.

View Source
const (
	RoleSystem    = "system"
	RoleUser      = "user"
	RoleAssistant = "assistant"
	RoleTool      = "tool"
)

Chat roles.

View Source
const DefaultBaseURL = "https://www.nodedata.ai/api/v1"

DefaultBaseURL is the production API root.

View Source
const DefaultTolerance = 5 * time.Minute

DefaultTolerance is the maximum accepted clock skew between the signature timestamp and now.

View Source
const SignatureHeader = "nd-signature"

SignatureHeader is the header carrying the delivery signature.

View Source
const Version = "0.1.0"

Version is the SDK version, sent on every request.

Variables

Scopes lists every scope the API recognises.

View Source
var StopIteration = errors.New("nodedata: stop iteration")

StopIteration ends a Page.Each walk early without reporting an error.

WebhookEventTypes lists every event Node Data can deliver.

Functions

func Ptr

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

Ptr returns a pointer to v. Handy for the optional pointer fields above:

params := nodedata.CreateAssetParams{PriceCents: nodedata.Ptr(int64(0))}

func SignPayload

func SignPayload(payload, secret string, at time.Time) string

SignPayload produces a signature header for a payload, mirroring what the platform sends. Pass the zero time.Time to sign as of now.

Types

type APIError

type APIError struct {
	Status    int
	Code      string
	Message   string
	RequestID string
	// Body is the decoded JSON error body, or the raw string when the response
	// was not JSON.
	Body any
	// RetryAfter is populated from the Retry-After header, mainly on 429.
	RetryAfter *time.Duration
}

APIError is any non-2xx response. Status and Code identify what went wrong; RequestID is worth quoting in a support request.

func (*APIError) Error

func (e *APIError) Error() string

type Account

type Account struct {
	ID        string     `json:"id"`
	Slug      string     `json:"slug"`
	Name      string     `json:"name"`
	Email     string     `json:"email"`
	CreatedAt string     `json:"created_at"`
	Key       AccountKey `json:"key"`
}

Account is the owner of the API key.

type AccountKey

type AccountKey struct {
	Mode string `json:"mode"`
	// Scopes is empty when the key has unrestricted access.
	Scopes []string `json:"scopes"`
}

AccountKey describes the key used to authenticate the current request.

type AccountResource

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

AccountResource covers the authenticated account.

func (*AccountResource) Retrieve

func (a *AccountResource) Retrieve(ctx context.Context, opts ...RequestOption) (*Account, error)

Retrieve reports who this key belongs to, plus its mode and scopes.

func (*AccountResource) Usage

func (a *AccountResource) Usage(ctx context.Context, days int, opts ...RequestOption) (*Usage, error)

Usage reports inference spend, rolled up and per model. Pass 0 days for the server default.

type Asset

type Asset struct {
	ID                 string     `json:"id"`
	Slug               string     `json:"slug"`
	Title              string     `json:"title"`
	Description        string     `json:"description"`
	Type               string     `json:"type"`
	Category           string     `json:"category"`
	License            string     `json:"license"`
	Version            string     `json:"version"`
	Price              Price      `json:"price"`
	Tags               []string   `json:"tags"`
	Frameworks         []string   `json:"frameworks"`
	Hardware           []string   `json:"hardware"`
	Sensors            []string   `json:"sensors"`
	ROSCompatible      bool       `json:"ros_compatible"`
	JetsonCompatible   bool       `json:"jetson_compatible"`
	IsaacSimCompatible bool       `json:"isaac_sim_compatible"`
	File               *AssetFile `json:"file"`
	Downloads          int64      `json:"downloads"`
	Featured           bool       `json:"featured"`
	Author             *Author    `json:"author"`
	CreatedAt          string     `json:"created_at"`
	UpdatedAt          string     `json:"updated_at"`
}

Asset is a marketplace listing: model, dataset, policy, or workflow.

type AssetFile

type AssetFile struct {
	Name      string  `json:"name"`
	Size      *int64  `json:"size"`
	Extension *string `json:"extension"`
}

AssetFile describes the downloadable artifact attached to a listing.

type AuthenticationError

type AuthenticationError struct{ *APIError }

AuthenticationError is a 401 — missing, malformed, revoked, or expired key.

func (*AuthenticationError) Unwrap

func (e *AuthenticationError) Unwrap() error

type Author

type Author struct {
	Slug string `json:"slug"`
	Name string `json:"name"`
}

Author is the creator of a listing.

type BadRequestError

type BadRequestError struct{ *APIError }

BadRequestError is a 400.

func (*BadRequestError) Unwrap

func (e *BadRequestError) Unwrap() error

type ChatChoice

type ChatChoice struct {
	Index        int         `json:"index"`
	Message      ChatMessage `json:"message"`
	FinishReason *string     `json:"finish_reason"`
}

ChatChoice is one completion candidate.

type ChatChunkChoice

type ChatChunkChoice struct {
	Index        int       `json:"index"`
	Delta        ChatDelta `json:"delta"`
	FinishReason *string   `json:"finish_reason"`
}

ChatChunkChoice is one candidate within a streamed chunk.

type ChatCompletion

type ChatCompletion struct {
	ID      string       `json:"id"`
	Object  string       `json:"object"`
	Created int64        `json:"created"`
	Model   string       `json:"model"`
	Choices []ChatChoice `json:"choices"`
	Usage   *ChatUsage   `json:"usage,omitempty"`
}

ChatCompletion is a buffered, non-streaming completion.

type ChatCompletionChunk

type ChatCompletionChunk struct {
	ID      string            `json:"id"`
	Object  string            `json:"object"`
	Created int64             `json:"created"`
	Model   string            `json:"model"`
	Choices []ChatChunkChoice `json:"choices"`
}

ChatCompletionChunk is a single server-sent event from a streamed completion.

func (*ChatCompletionChunk) String

func (c *ChatCompletionChunk) String() string

String returns the text content of the first choice, which is what callers almost always want from a chunk.

type ChatCompletionParams

type ChatCompletionParams struct {
	Model          string          `json:"model"`
	Messages       []ChatMessage   `json:"messages"`
	Temperature    *float64        `json:"temperature,omitempty"`
	TopP           *float64        `json:"top_p,omitempty"`
	MaxTokens      *int            `json:"max_tokens,omitempty"`
	Stop           []string        `json:"stop,omitempty"`
	ResponseFormat *ResponseFormat `json:"response_format,omitempty"`
}

ChatCompletionParams is the request body for an inference call.

type ChatDelta

type ChatDelta struct {
	Role    string `json:"role,omitempty"`
	Content string `json:"content,omitempty"`
}

ChatDelta is the incremental content in a streamed chunk.

type ChatMessage

type ChatMessage struct {
	Role    string `json:"role"`
	Content string `json:"content"`
	Name    string `json:"name,omitempty"`
}

ChatMessage is one turn in a conversation.

type ChatStream

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

ChatStream is a live server-sent-event stream of completion chunks.

stream, err := nd.Inference.Stream(ctx, params)
if err != nil { return err }
defer stream.Close()
for stream.Next() {
    fmt.Print(stream.Current())
}
return stream.Err()

func (*ChatStream) Close

func (s *ChatStream) Close() error

Close releases the underlying connection. Safe to call more than once.

func (*ChatStream) Current

func (s *ChatStream) Current() *ChatCompletionChunk

Current returns the chunk from the most recent Next.

func (*ChatStream) Err

func (s *ChatStream) Err() error

Err reports why the stream stopped, or nil if it finished normally.

func (*ChatStream) Next

func (s *ChatStream) Next() bool

Next advances to the next chunk, reporting false when the stream ends or fails. Check Err after the loop to tell those apart.

type ChatUsage

type ChatUsage struct {
	PromptTokens     int `json:"prompt_tokens"`
	CompletionTokens int `json:"completion_tokens"`
	TotalTokens      int `json:"total_tokens"`
}

ChatUsage is the token accounting for a completion.

type Client

type Client struct {
	Models           *Models
	Datasets         *Datasets
	Account          *AccountResource
	Payouts          *PayoutsResource
	WebhookEndpoints *WebhookEndpoints
	Inference        *Inference
	// contains filtered or unexported fields
}

Client is the Node Data API client. It is safe for concurrent use.

func New

func New(cfg Config) (*Client, error)

New builds a client. It returns an error when no API key is available.

func (*Client) BaseURL

func (c *Client) BaseURL() string

BaseURL reports the API root in use.

func (*Client) Do

func (c *Client) Do(ctx context.Context, method, path string, req *RawRequest, out any, opts ...RequestOption) error

Do is the escape hatch for endpoints this SDK does not model yet. It applies the same auth, retries, and error mapping as the typed methods and decodes the response into out. Pass a nil out to discard the body.

var result map[string]any
err := client.Do(ctx, "GET", "/some/new/endpoint", nil, &result)

func (*Client) MaskedKey

func (c *Client) MaskedKey() string

MaskedKey returns the key with everything after the prefix hidden. Safe to log.

func (*Client) Mode

func (c *Client) Mode() string

Mode reports ModeTest or ModeLive, read from the key prefix without a network call. Unrecognised prefixes report ModeUnknown.

func (*Client) Ping

func (c *Client) Ping(ctx context.Context) PingResult

Ping makes one round trip to confirm the key works and report what it can do. It never returns an error — failures land in the result, so this is safe to call in a health check.

type Config

type Config struct {
	// APIKey is your nd_test_… or nd_live_… key. Defaults to
	// $NODE_DATA_API_KEY. Create one at
	// https://www.nodedata.ai/dashboard/api-keys
	APIKey string
	// BaseURL overrides the API root. Defaults to $NODE_DATA_BASE_URL, then
	// DefaultBaseURL.
	BaseURL string
	// Timeout is the per-request timeout. Defaults to DefaultTimeout.
	Timeout time.Duration
	// MaxRetries bounds retries for transient failures (429, 5xx, network).
	// Defaults to DefaultMaxRetries. Set NoRetries to disable.
	MaxRetries int
	// NoRetries disables retries entirely, since a zero MaxRetries cannot be
	// told apart from "unset".
	NoRetries bool
	// Headers are sent on every request.
	Headers map[string]string
	// HTTPClient replaces the default client. Useful for proxies, custom
	// transports, and tests.
	HTTPClient *http.Client
	// OnRequest is called before each attempt, including retries.
	OnRequest func(RequestInfo)
}

Config configures a Client. Every field is optional except APIKey, which falls back to the NODE_DATA_API_KEY environment variable.

type ConflictError

type ConflictError struct{ *APIError }

ConflictError is a 409.

func (*ConflictError) Unwrap

func (e *ConflictError) Unwrap() error

type ConnectionError

type ConnectionError struct {
	Message string
	Cause   error
}

ConnectionError means the request never produced an HTTP response — DNS failure, connection refused, offline, or cancelled by the caller.

func (*ConnectionError) Error

func (e *ConnectionError) Error() string

func (*ConnectionError) Unwrap

func (e *ConnectionError) Unwrap() error

type CreateAssetParams

type CreateAssetParams struct {
	Title       string `json:"title"`
	Description string `json:"description"`
	Type        string `json:"type"`
	Category    string `json:"category,omitempty"`
	License     string `json:"license,omitempty"`
	Version     string `json:"version,omitempty"`
	// PriceCents is USD cents. 0 is free; paid listings start at 100 ($1.00).
	// Pointer so an explicit 0 is distinguishable from "unset".
	PriceCents *int64   `json:"price_cents,omitempty"`
	Frameworks []string `json:"frameworks,omitempty"`
	// StoragePath is the key returned by the upload-url flow. Must be under
	// your own folder.
	StoragePath   string `json:"storage_path,omitempty"`
	FileName      string `json:"file_name,omitempty"`
	FileSize      *int64 `json:"file_size,omitempty"`
	FileExtension string `json:"file_extension,omitempty"`
}

CreateAssetParams publishes a new listing.

type CreateWebhookEndpointParams

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

CreateWebhookEndpointParams registers a new endpoint.

type Datasets

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

Datasets are listings with a dataset type. Requires the datasets:* scopes.

func (*Datasets) Create

func (d *Datasets) Create(ctx context.Context, params CreateAssetParams, opts ...RequestOption) (*Asset, error)

Create publishes a dataset. The type field is set for you.

func (*Datasets) List

func (d *Datasets) List(ctx context.Context, params *ListAssetsParams, opts ...RequestOption) (*Page[Asset], error)

List returns a page of datasets.

func (*Datasets) Retrieve

func (d *Datasets) Retrieve(ctx context.Context, idOrSlug string, opts ...RequestOption) (*Asset, error)

Retrieve fetches one dataset by id or slug.

type DeleteResult

type DeleteResult struct {
	ID      string `json:"id"`
	Deleted bool   `json:"deleted"`
}

DeleteResult is returned by the delete endpoints.

type Error

type Error struct {
	Message string
}

Error is the base type for every error this package returns. Use it with errors.As to catch anything originating from the SDK.

func (*Error) Error

func (e *Error) Error() string

type Inference

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

Inference is Node Data's OpenAI-compatible inference API.

The wire format is OpenAI-compatible, so any OpenAI client pointed at https://www.nodedata.ai/api/v1 works too — this namespace just saves a second dependency. Requires a premium key (inference:run).

func (*Inference) Create

func (i *Inference) Create(ctx context.Context, params ChatCompletionParams, opts ...RequestOption) (*ChatCompletion, error)

Create runs a single buffered completion.

func (*Inference) Models

func (i *Inference) Models(ctx context.Context, opts ...RequestOption) ([]InferenceModel, error)

Models returns the Node model catalog with live pricing.

func (*Inference) Stream

func (i *Inference) Stream(ctx context.Context, params ChatCompletionParams, opts ...RequestOption) (*ChatStream, error)

Stream runs a completion and yields parsed SSE chunks. The caller must Close the returned stream.

Streams are never retried mid-flight — a half-emitted response cannot be safely resumed — so the caller's context is the only cancellation path.

func (*Inference) StreamToText

func (i *Inference) StreamToText(ctx context.Context, params ChatCompletionParams, onToken func(string), opts ...RequestOption) (string, error)

StreamToText collects a stream into the full assistant message. Pass a non-nil onToken to observe tokens as they arrive.

type InferenceModel

type InferenceModel struct {
	ID            string           `json:"id"`
	Label         string           `json:"label"`
	Description   string           `json:"description"`
	ContextWindow int              `json:"context_window"`
	Pricing       InferencePricing `json:"pricing"`
}

InferenceModel is an entry in the Node model catalog.

type InferencePricing

type InferencePricing struct {
	InputPer1MUSD  float64 `json:"input_per_1m_usd"`
	OutputPer1MUSD float64 `json:"output_per_1m_usd"`
}

InferencePricing is per-million-token pricing in USD.

type ListAssetsParams

type ListAssetsParams struct {
	// Limit is 1–100. The server defaults to 20.
	Limit int
	// Q is a free-text search across title, description, and tags.
	Q        string
	Type     string
	Category string
	Cursor   string
}

ListAssetsParams filters a listing query. Zero values are omitted.

type Models

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

Models covers marketplace listings.

Models and Datasets are the same underlying resource — the type field distinguishes them — but they need different scopes, so both namespaces exist with the right defaults.

func (*Models) Create

func (m *Models) Create(ctx context.Context, params CreateAssetParams, opts ...RequestOption) (*Asset, error)

Create publishes a listing. Requires models:upload.

func (*Models) Delete

func (m *Models) Delete(ctx context.Context, idOrSlug string, opts ...RequestOption) (*DeleteResult, error)

Delete unpublishes a listing. Purchase history is preserved.

func (*Models) List

func (m *Models) List(ctx context.Context, params *ListAssetsParams, opts ...RequestOption) (*Page[Asset], error)

List returns a page of listings. Requires models:read.

func (*Models) Retrieve

func (m *Models) Retrieve(ctx context.Context, idOrSlug string, opts ...RequestOption) (*Asset, error)

Retrieve fetches one listing by id or URL slug.

func (*Models) Update

func (m *Models) Update(ctx context.Context, idOrSlug string, params UpdateAssetParams, opts ...RequestOption) (*Asset, error)

Update patches a listing. Requires listings:write.

type NotFoundError

type NotFoundError struct{ *APIError }

NotFoundError is a 404.

func (*NotFoundError) Unwrap

func (e *NotFoundError) Unwrap() error

type Page

type Page[T any] struct {
	Items      []T
	NextCursor string
	HasMore    bool
	// contains filtered or unexported fields
}

Page is one cursor-paginated result set, plus the means to walk the rest. Items is the current page; Each and All continue through the collection.

func (*Page[T]) All

func (p *Page[T]) All(ctx context.Context, limit int) ([]T, error)

All collects every remaining item into one slice. A limit of 0 or less means no limit — beware large collections.

func (*Page[T]) Each

func (p *Page[T]) Each(ctx context.Context, fn func(T) error) error

Each calls fn for every remaining item across every page. Returning StopIteration from fn stops the walk cleanly; any other error stops it and is returned as-is.

func (*Page[T]) Next

func (p *Page[T]) Next(ctx context.Context) (*Page[T], error)

Next fetches the following page. It returns (nil, nil) once the collection is exhausted, so a loop reads:

for page != nil {
    ...
    page, err = page.Next(ctx)
}

type PaymentRequiredError

type PaymentRequiredError struct{ *APIError }

PaymentRequiredError is a 402 — the key exists but has not been activated. Premium (live inference) keys need a one-time checkout at https://www.nodedata.ai/dashboard/api-keys

func (*PaymentRequiredError) Unwrap

func (e *PaymentRequiredError) Unwrap() error

type PayoutSale

type PayoutSale struct {
	ID                 string           `json:"id"`
	AmountCents        int64            `json:"amount_cents"`
	PlatformFeeCents   int64            `json:"platform_fee_cents"`
	CreatorAmountCents int64            `json:"creator_amount_cents"`
	Currency           string           `json:"currency"`
	CreatedAt          string           `json:"created_at"`
	Asset              *PayoutSaleAsset `json:"asset"`
}

PayoutSale is a single completed sale.

type PayoutSaleAsset

type PayoutSaleAsset struct {
	Slug  string `json:"slug"`
	Title string `json:"title"`
}

PayoutSaleAsset identifies the listing a sale belongs to.

type PayoutSummary

type PayoutSummary struct {
	GrossCents       int64  `json:"gross_cents"`
	PlatformFeeCents int64  `json:"platform_fee_cents"`
	NetCents         int64  `json:"net_cents"`
	SaleCount        int64  `json:"sale_count"`
	Currency         string `json:"currency"`
}

PayoutSummary is the 25/75 split rolled up across sales.

type Payouts

type Payouts struct {
	// PayoutsPaused is true while platform payouts are paused. Balances still
	// accrue.
	PayoutsPaused bool          `json:"payouts_paused"`
	Summary       PayoutSummary `json:"summary"`
	Sales         []PayoutSale  `json:"sales,omitempty"`
	Items         []PayoutSale  `json:"items,omitempty"`
}

Payouts is the response from GET /payouts.

func (*Payouts) AllSales

func (p *Payouts) AllSales() []PayoutSale

AllSales returns the sale list regardless of which field the API populated.

type PayoutsResource

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

PayoutsResource covers creator earnings.

func (*PayoutsResource) Retrieve

func (p *PayoutsResource) Retrieve(ctx context.Context, limit int, opts ...RequestOption) (*Payouts, error)

Retrieve returns completed sales and the 25/75 split. Requires payouts:read. Pass 0 for the server default limit.

type PermissionError

type PermissionError struct{ *APIError }

PermissionError is a 403 — the key is valid but lacks the required scope.

func (*PermissionError) RequiredScope

func (e *PermissionError) RequiredScope() string

RequiredScope reports the scope the endpoint asked for, parsed out of the error message. Returns "" when the message does not name one.

func (*PermissionError) Unwrap

func (e *PermissionError) Unwrap() error

type PingResult

type PingResult struct {
	OK      bool
	Latency time.Duration
	Account string
	// Scopes holds ["*"] when the key is unrestricted.
	Scopes []string
	// Err is the failure, when OK is false.
	Err error
}

PingResult is what Ping reports back.

type Price

type Price struct {
	Amount   int64  `json:"amount"`
	Currency string `json:"currency"`
}

Price is an amount in the smallest currency unit (USD cents).

type RateLimitError

type RateLimitError struct{ *APIError }

RateLimitError is a 429. Check RetryAfter on the embedded APIError for how long the server asked you to wait.

func (*RateLimitError) Unwrap

func (e *RateLimitError) Unwrap() error

type RawRequest

type RawRequest struct {
	Query url.Values
	Body  any
}

RawRequest is the query and body for Client.Do.

type RequestInfo

type RequestInfo struct {
	Method  string
	URL     string
	Attempt int
}

RequestInfo is handed to Config.OnRequest before every attempt, including retries. Useful for logging and metrics.

type RequestOption

type RequestOption func(*requestConfig)

RequestOption overrides a client default for one call.

func WithHeader

func WithHeader(key, value string) RequestOption

WithHeader adds or replaces a header on a single call.

func WithMaxRetries

func WithMaxRetries(n int) RequestOption

WithMaxRetries overrides the client retry count for a single call. Zero disables retries.

func WithTimeout

func WithTimeout(d time.Duration) RequestOption

WithTimeout overrides the client timeout for a single call.

type ResponseFormat

type ResponseFormat struct {
	Type string `json:"type"` // "text" or "json_object"
}

ResponseFormat pins the shape of the model's reply.

type ServerError

type ServerError struct{ *APIError }

ServerError is any 5xx.

func (*ServerError) Unwrap

func (e *ServerError) Unwrap() error

type TimeoutError

type TimeoutError struct {
	ConnectionError
	Timeout time.Duration
}

TimeoutError means a request exceeded its configured timeout. It unwraps to a *ConnectionError, so code that only cares about "the request never landed" can match the broader type.

func (*TimeoutError) Unwrap

func (e *TimeoutError) Unwrap() error

type UpdateAssetParams

type UpdateAssetParams struct {
	Title       *string  `json:"title,omitempty"`
	Description *string  `json:"description,omitempty"`
	Category    *string  `json:"category,omitempty"`
	License     *string  `json:"license,omitempty"`
	Version     *string  `json:"version,omitempty"`
	PriceCents  *int64   `json:"price_cents,omitempty"`
	Frameworks  []string `json:"frameworks,omitempty"`
	Status      *string  `json:"status,omitempty"` // "published" or "draft"
}

UpdateAssetParams patches a listing. Every field is optional; nil means "leave unchanged".

type UpdateWebhookEndpointParams

type UpdateWebhookEndpointParams struct {
	URL         *string  `json:"url,omitempty"`
	Events      []string `json:"events,omitempty"`
	Description *string  `json:"description,omitempty"`
	Enabled     *bool    `json:"enabled,omitempty"`
}

UpdateWebhookEndpointParams patches an endpoint. nil means "leave unchanged".

type Usage

type Usage struct {
	WindowDays int            `json:"window_days"`
	Summary    UsageSummary   `json:"summary"`
	ByModel    []UsageByModel `json:"by_model"`
}

Usage is the response from GET /usage.

type UsageByModel

type UsageByModel struct {
	Model            string  `json:"model"`
	Calls            int64   `json:"calls"`
	PromptTokens     int64   `json:"prompt_tokens"`
	CompletionTokens int64   `json:"completion_tokens"`
	TotalTokens      int64   `json:"total_tokens"`
	CostMicros       int64   `json:"cost_micros"`
	CostUSD          float64 `json:"cost_usd"`
}

UsageByModel is inference spend for a single model.

type UsageSummary

type UsageSummary struct {
	Calls       int64   `json:"calls"`
	TotalTokens int64   `json:"total_tokens"`
	CostMicros  int64   `json:"cost_micros"`
	CostUSD     float64 `json:"cost_usd"`
}

UsageSummary is inference spend rolled up across models.

type VerifyOptions

type VerifyOptions struct {
	// Payload must be the exact bytes of the request body. Never re-serialize
	// parsed JSON — key order and whitespace both change the signature.
	Payload string
	// Signature is the nd-signature header value.
	Signature string
	// Secret is the endpoint's signing secret (whsec_…).
	Secret string
	// Tolerance defaults to DefaultTolerance when zero.
	Tolerance time.Duration
	// Now overrides the current time. For tests.
	Now time.Time
}

VerifyOptions configures VerifyWebhook.

type WebhookDelivery

type WebhookDelivery struct {
	ID             string          `json:"id"`
	Type           string          `json:"type"`
	Status         string          `json:"status"`
	ResponseStatus *int            `json:"response_status"`
	Attempts       int             `json:"attempts"`
	CreatedAt      string          `json:"created_at"`
	Data           json.RawMessage `json:"data,omitempty"`
}

WebhookDelivery is one attempted delivery.

type WebhookEndpoint

type WebhookEndpoint struct {
	ID          string   `json:"id"`
	URL         string   `json:"url"`
	Events      []string `json:"events"`
	Description *string  `json:"description"`
	Enabled     bool     `json:"enabled"`
	CreatedAt   string   `json:"created_at"`
	// Secret is returned only on create. Store it — it is never shown again.
	Secret string `json:"secret,omitempty"`
}

WebhookEndpoint is a registered delivery target.

type WebhookEndpoints

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

WebhookEndpoints manages delivery targets. Every call needs webhooks:write.

func (*WebhookEndpoints) Create

Create registers an endpoint. The response carries Secret exactly once — persist it immediately, it cannot be retrieved later.

func (*WebhookEndpoints) Delete

func (w *WebhookEndpoints) Delete(ctx context.Context, id string, opts ...RequestOption) (*DeleteResult, error)

Delete removes an endpoint.

func (*WebhookEndpoints) Deliveries

func (w *WebhookEndpoints) Deliveries(ctx context.Context, id string, limit int, opts ...RequestOption) ([]WebhookDelivery, error)

Deliveries lists recent delivery attempts for an endpoint. Pass 0 for the server default limit.

func (*WebhookEndpoints) List

List returns every registered endpoint.

func (*WebhookEndpoints) Retrieve

func (w *WebhookEndpoints) Retrieve(ctx context.Context, id string, opts ...RequestOption) (*WebhookEndpoint, error)

Retrieve fetches one endpoint.

func (*WebhookEndpoints) Update

Update patches an endpoint.

type WebhookEvent

type WebhookEvent struct {
	ID      string          `json:"id"`
	Type    string          `json:"type"`
	Created int64           `json:"created"`
	Data    json.RawMessage `json:"data"`
}

WebhookEvent is the JSON body Node Data POSTs to your endpoint. Data is left raw so you can unmarshal it into whatever shape the event carries.

func VerifyRequest

func VerifyRequest(r *http.Request, secret string, tolerance ...time.Duration) (*WebhookEvent, error)

VerifyRequest verifies an inbound *http.Request. It reads the body itself so you cannot accidentally verify against re-serialized JSON. Pass a tolerance to override DefaultTolerance.

func VerifyWebhook

func VerifyWebhook(opts VerifyOptions) (*WebhookEvent, error)

VerifyWebhook validates a delivery and returns the parsed event. It fails when the header is malformed, the timestamp falls outside tolerance (a replay), or no signature matches.

type WebhookVerificationError

type WebhookVerificationError struct {
	Code    string
	Message string
}

WebhookVerificationError is returned by VerifyWebhook when a delivery fails validation. Code is one of the Webhook* constants above.

func (*WebhookVerificationError) Error

func (e *WebhookVerificationError) Error() string

Jump to

Keyboard shortcuts

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