> ## Documentation Index
> Fetch the complete documentation index at: https://docs.moneyline.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Go SDK

> Official Go SDK for the Moneyline API — idiomatic, context-aware, with generated types for every response body.

## Installation

```bash theme={null}
go get github.com/paragonhq/moneylined/sdks/go
```

Requires Go 1.24 or newer.

## Quick start

```go theme={null}
package main

import (
	"context"
	"log"
	"os"

	moneyline "github.com/paragonhq/moneylined/sdks/go/moneyline"
)

func main() {
	// An empty key falls back to the MONEYLINE_API_KEY environment variable.
	client, err := moneyline.NewClient("")
	if err != nil {
		log.Fatal(err)
	}

	ctx := context.Background()

	submission, err := client.Submissions.Create(ctx, &moneyline.CreateSubmissionParams{
		ExternalID: "acme-001",
		Name:       "Acme Corp",
	})
	if err != nil {
		log.Fatal(err)
	}

	f, err := os.Open("bank-statement.pdf")
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

	if _, err := client.Documents.Upload(ctx, submission.Id, "bank-statement.pdf", f); err != nil {
		log.Fatal(err)
	}

	scorecard, err := client.Analytics.GetScorecard(ctx, submission.Id)
	if err != nil {
		log.Fatal(err)
	}
	log.Println(scorecard)
}
```

> **Response structs come from the generated `openapi` subpackage** and use the
> API's own field casing: `submission.Id`, `submission.ExternalId` — not `ID` /
> `ExternalID`. Request param structs are hand-written and do use Go casing
> (`CreateSubmissionParams.ExternalID`). The two differ on purpose.

## Configuration

`NewClient` takes the API key plus functional options.

```go theme={null}
client, err := moneyline.NewClient(
	"ml_live_xxxxx",
	moneyline.WithBaseURL("https://api.moneyline.co"),
	moneyline.WithMaxRetries(3),
	moneyline.WithHTTPClient(&http.Client{Timeout: 60 * time.Second}),
)
```

| Option | Description |
| - | - |
| `WithBaseURL(url)` | Override the API base URL (default `MONEYLINE_BASE_URL`, else production) |
| `WithMaxRetries(n)` | Retries on 429/5xx |
| `WithHTTPClient(hc)` | Supply your own `*http.Client`, e.g. for timeouts or tracing |

## Pagination

List methods that accept a `*Params` struct return `*PaginatedResponse[T]` with `Data`, `HasMore`, `Cursor`, and `TotalCount`.

```go theme={null}
page, err := client.Transactions.List(ctx, &moneyline.ListTransactionsParams{
	SubmissionID: submission.Id,
	Limit:        100,
})
for _, txn := range page.Data {
	log.Println(txn.Amount)
}
```

> **Cursor paging does not currently advance.** `PaginatedResponse.Cursor` is
> deserialized from `cursor`, but the API returns the next cursor as
> `next_cursor`, so the field stays empty and a cursor loop stops after one
> page. Until that is fixed, read `HasMore` and raise `Limit`.

The simpler list methods (`Categories.List`, `Funders.List`, `Rules.List`, `Webhooks.List`, `Entities.List`, `Integrations.List`) take no params and return a plain slice.

## Resources

### Submissions

```go theme={null}
sub, err := client.Submissions.Create(ctx, &moneyline.CreateSubmissionParams{
	ExternalID:  "acme-001",
	Name:        "Acme Corp",
	CompanyInfo: map[string]interface{}{"ein": "12-3456789"},
	Metadata:    map[string]interface{}{"source": "broker-portal"},
})

fetched, err := client.Submissions.Get(ctx, sub.Id)

updated, err := client.Submissions.Update(ctx, sub.Id, &moneyline.UpdateSubmissionParams{
	Name: "Acme Corp LLC",
})

page, err := client.Submissions.List(ctx, &moneyline.ListSubmissionsParams{
	Status: "processed",
	Limit:  25,
})

err = client.Submissions.Delete(ctx, sub.Id)
err = client.Submissions.StartWorkflow(ctx, sub.Id)

status, err := client.Submissions.GetProcessingStatus(ctx, sub.Id)
err = client.Submissions.Enrich(ctx, sub.Id, "high")

dupes, err := client.Submissions.FindDuplicates(ctx, sub.Id)
```

Note that `Get` is the Go name for what Node and Python call `retrieve`, and there is no `waitForProcessing` helper — poll `GetProcessingStatus` or use [webhooks](https://docs.moneyline.co/guides/webhooks.md).

### Documents

```go theme={null}
doc, err := client.Documents.Upload(ctx, sub.Id, "statement.pdf", reader)

fetched, err := client.Documents.Get(ctx, doc.Id)

page, err := client.Documents.List(ctx, &moneyline.ListDocumentsParams{
	SubmissionID: sub.Id,
	Limit:        25,
})

url, err := client.Documents.GetDownloadURL(ctx, doc.Id)
reclassified, err := client.Documents.Reclassify(ctx, doc.Id, "tax_return")
err = client.Documents.Parse(ctx, doc.Id)
parts, err := client.Documents.Split(ctx, doc.Id)
err = client.Documents.Delete(ctx, doc.Id)
```

### Transactions

```go theme={null}
page, err := client.Transactions.List(ctx, &moneyline.ListTransactionsParams{
	SubmissionID: sub.Id,
})

txn, err := client.Transactions.Get(ctx, txnID)

// Enrich without persisting (max 249 per request).
enriched, err := client.Transactions.Enrich(ctx, &moneyline.EnrichTransactionsParams{
	Transactions: []moneyline.TransactionInput{
		{Description: "AMZN Mktp US*2A4B", Amount: -42.11},
	},
})

// Persist against a submission.
err = client.Transactions.Create(ctx, sub.Id, []moneyline.TransactionInput{
	{Description: "DEPOSIT", Amount: 5000},
})

err = client.Transactions.Delete(ctx, txnID)
```

### Analytics

```go theme={null}
scorecard, err  := client.Analytics.GetScorecard(ctx, sub.Id)
cashflow, err   := client.Analytics.GetCashflow(ctx, sub.Id)
risk, err       := client.Analytics.GetRiskScore(ctx, sub.Id)
balances, err   := client.Analytics.GetBalances(ctx, sub.Id)
anomalies, err  := client.Analytics.GetAnomalies(ctx, sub.Id)
statistics, err := client.Analytics.GetStatistics(ctx, sub.Id)
forecast, err   := client.Analytics.GetForecast(ctx, sub.Id)
bank, err       := client.Analytics.GetBankSummary(ctx, sub.Id)
recurring, err  := client.Analytics.GetRecurring(ctx, sub.Id)
merchants, err  := client.Analytics.GetMerchants(ctx, sub.Id)
categories, err := client.Analytics.GetCategories(ctx, sub.Id)
metrics, err    := client.Analytics.GetMetrics(ctx, sub.Id)
coverage, err   := client.Analytics.GetCoverage(ctx, sub.Id)
financials, err := client.Analytics.GetFinancials(ctx, sub.Id)
benchmarks, err := client.Analytics.GetBenchmarks(ctx, sub.Id)
```

### Policies and rules

The Go SDK exposes a narrower policy surface than Node and Python — `EvaluateAll` lives on `Rules`.

```go theme={null}
evaluations, err := client.Policies.Evaluate(ctx, sub.Id)
err = client.Policies.Progress(ctx, sub.Id, "approve")

rule, err := client.Rules.Create(ctx, &moneyline.CreateRuleParams{
	Name:     "Low average balance",
	Metric:   "average_daily_balance",
	Operator: "lt",
	Threshold: 5000,
})

fetched, err := client.Rules.Get(ctx, rule.Id)
updated, err := client.Rules.Update(ctx, rule.Id, &moneyline.UpdateRuleParams{Threshold: 7500})
rules, err := client.Rules.List(ctx)
err = client.Rules.Delete(ctx, rule.Id)

metrics, err := client.Rules.GetMetrics(ctx)
summary, err := client.Rules.EvaluateAll(ctx, sub.Id)
```

### Webhooks

```go theme={null}
hook, err := client.Webhooks.Create(ctx, &moneyline.CreateWebhookParams{
	URL:    "https://example.com/moneyline",
	Events: []string{"submission.processed", "document.parsed"},
})

fetched, err := client.Webhooks.Get(ctx, hook.Id)
hooks, err := client.Webhooks.List(ctx)
err = client.Webhooks.Delete(ctx, hook.Id)

result, err := client.Webhooks.Test(ctx, hook.Id)
types, err := client.Webhooks.ListEventTypes(ctx)

deliveries, err := client.Webhooks.ListDeliveries(ctx, hook.Id, &moneyline.ListParams{Limit: 50})
replayed, err := client.Webhooks.Replay(ctx, hook.Id, deliveryID)
```

### Merchants, categories, funders

```go theme={null}
merchant, err := client.Merchants.Get(ctx, merchantID)
results, err := client.Merchants.Search(ctx, "amazon")
groups, err := client.Merchants.GetGroups(ctx)
resolved, err := client.Merchants.Extract(ctx, "AMZN Mktp US*2A4B9")

categories, err := client.Categories.List(ctx)

funders, err := client.Funders.List(ctx)
funder, err := client.Funders.Get(ctx, funderID)
linked, err := client.Funders.GetLinked(ctx)
submitted, err := client.Funders.Submit(ctx, funderID, sub.Id)
funderSubs, err := client.Funders.ListSubmissions(ctx, funderID)
```

### Integrations, entities, ISO applications

```go theme={null}
integration, err := client.Integrations.Create(ctx, &moneyline.CreateIntegrationParams{
	Type: "plaid",
	Name: "Plaid production",
})
integrations, err := client.Integrations.List(ctx)
updated, err := client.Integrations.Update(ctx, integration.Id, &moneyline.UpdateIntegrationParams{
	Name: "Plaid sandbox",
})
links, err := client.Integrations.GetLinks(ctx, integration.Id)
imported, err := client.Integrations.Import(ctx, sub.Id, "plaid", map[string]interface{}{
	"access_token": "...",
})

entities, err := client.Entities.List(ctx)
entity, err := client.Entities.Get(ctx, entityID)
err = client.Entities.BulkSave(ctx, []map[string]interface{}{{"name": "Beneficial owner"}})

app, err := client.IsoApplications.Upload(ctx, "iso-application.pdf", reader, sub.Id)
fetchedApp, err := client.IsoApplications.Get(ctx, app.Id)
appResults, err := client.IsoApplications.Results(ctx, app.Id)
reprocessed, err := client.IsoApplications.Reprocess(ctx, app.Id)
```

## Error handling

API failures return `*moneyline.Error`, carrying `Message`, `Type`, `Code`, `Param`, `StatusCode`, and `RequestID`.

```go theme={null}
_, err := client.Submissions.Get(ctx, "does-not-exist")

var apiErr *moneyline.Error
if errors.As(err, &apiErr) {
	switch {
	case apiErr.IsAuthError():  // 401
		log.Println("bad API key")
	case apiErr.IsNotFound():   // 404
		log.Println("no such submission")
	case apiErr.IsValidation(): // 400 / 422
		log.Println("invalid request:", apiErr.Message)
	case apiErr.IsRateLimit():  // 429
		log.Println("rate limited after retries")
	default:
		log.Println(apiErr.Code, apiErr.RequestID)
	}
}
```

## Webhook signature verification

```go theme={null}
func handler(w http.ResponseWriter, r *http.Request) {
	body, err := io.ReadAll(r.Body)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	if err := moneyline.VerifySignature(
		body,
		r.Header.Get("X-Moneyline-Signature"),
		os.Getenv("MONEYLINE_WEBHOOK_SECRET"),
		5*time.Minute,
	); err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	w.WriteHeader(http.StatusOK)
}
```

## Generated types

Response bodies live in a separate `openapi` subpackage, generated from the spec with `go generate ./...`:

```go theme={null}
import "github.com/paragonhq/moneylined/sdks/go/moneyline/openapi"

var sub openapi.Submission
```

The subpackage exists to avoid struct name collisions with the hand-written types in `moneyline`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.