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

# Go SDK for KYC, AML and Transactions

> Quickstart for the official Didit Go SDK: typed calls for the server APIs with the standard library only, webhook verification and token checks.

The official Didit module for Go. It needs Go 1.22 or later and uses only the standard library. See the [server SDKs overview](https://docs.didit.me/integration/server-sdks/overview.md) for what every SDK shares.

## Install

```bash theme={null}
go get github.com/didit-protocol/sdk-go
```

The module is [`github.com/didit-protocol/sdk-go` on pkg.go.dev](https://pkg.go.dev/github.com/didit-protocol/sdk-go). Its source and changelog are in [didit-protocol/sdk-go](https://github.com/didit-protocol/sdk-go) on GitHub.

The package is named `didit`. Webhooks live in `github.com/didit-protocol/sdk-go/webhook`, Authentication in `.../authentication` and client options in `.../option`.

The sample on each [API reference](https://docs.didit.me/api-reference/overview.md) page is the body of a function: it uses `context`, `fmt` and `log` from the standard library and this module.

## Configure the client

`didit.NewClient()` reads `DIDIT_API_KEY` from the environment. Pass options to override it, or to set the organization and application ids that application-scoped calls (cases, networks, regulatory reports) need. Any option also works on a single call.

```go theme={null}
package main

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

	"github.com/didit-protocol/sdk-go"
	"github.com/didit-protocol/sdk-go/option"
)

func main() {
	client := didit.NewClient(
		option.WithAPIKey(os.Getenv("DIDIT_API_KEY")),
		option.WithOrganizationID(os.Getenv("DIDIT_ORGANIZATION_ID")),
		option.WithApplicationID(os.Getenv("DIDIT_APPLICATION_ID")),
		option.WithTimeout(30*time.Second),
		option.WithMaxRetries(2),
	)

	// Options for a single call.
	decision, err := client.Sessions.RetrieveDecision(context.Background(), "11111111-2222-3333-4444-555555555555",
		didit.SessionsRetrieveDecisionParams{}, option.WithTimeout(5*time.Second))
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(decision.Status)
}
```

Optional request fields are `didit.Opt` values: set them with `didit.String`, `didit.Int`, `didit.Bool`, `didit.Float` or `didit.Some`, and leave them out to send nothing.

## Create a session

Create a [verification session](https://docs.didit.me/sessions-api/create-session.md) for a workflow and send your user to its URL.

```go theme={null}
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/didit-protocol/sdk-go"
)

func main() {
	client := didit.NewClient()

	session, err := client.Sessions.Create(context.Background(), didit.SessionsCreateParams{
		WorkflowID: "11111111-2222-3333-4444-555555555555",
		VendorData: didit.String("user-123"),
		Callback:   didit.String("https://example.com/verification/done"),
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(session.URL)
}
```

## Read the decision

When the user finishes (or when the webhook arrives), [read the decision](https://docs.didit.me/sessions-api/retrieve-session.md). Every feature result is a slice with one entry per workflow node.

```go theme={null}
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/didit-protocol/sdk-go"
)

func main() {
	client := didit.NewClient()

	decision, err := client.Sessions.RetrieveDecision(context.Background(), "11111111-2222-3333-4444-555555555555",
		didit.SessionsRetrieveDecisionParams{})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(decision.Status)
	for _, verification := range decision.IDVerifications {
		fmt.Println(verification.Status)
	}
}
```

## Verify a webhook

Pass the raw body bytes and the request headers. `webhook.Verify` returns a `*didit.WebhookVerificationError` for anything that is not a valid, fresh delivery, and a typed event otherwise.

```go theme={null}
package main

import (
	"errors"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"

	"github.com/didit-protocol/sdk-go"
	"github.com/didit-protocol/sdk-go/webhook"
)

func main() {
	secret := os.Getenv("DIDIT_WEBHOOK_SECRET")
	http.HandleFunc("POST /webhooks/didit", func(w http.ResponseWriter, r *http.Request) {
		body, err := io.ReadAll(http.MaxBytesReader(w, r.Body, 1<<20))
		if err != nil {
			http.Error(w, "bad body", http.StatusBadRequest)
			return
		}
		event, err := webhook.Verify(body, r.Header, secret)
		var invalid *didit.WebhookVerificationError
		if errors.As(err, &invalid) {
			http.Error(w, string(invalid.Reason), http.StatusBadRequest)
			return
		} else if err != nil {
			http.Error(w, "error", http.StatusInternalServerError)
			return
		}
		if e, ok := event.(*webhook.StatusUpdatedEvent); ok {
			fmt.Println(e.SessionID, e.Status)
		}
		w.WriteHeader(http.StatusOK)
	})
	log.Fatal(http.ListenAndServe(":8080", nil))
}
```

While you rotate the secret, add the old one with `webhook.WithSecrets`. See [Webhooks](https://docs.didit.me/integration/webhooks.md) for the events and the signature scheme.

## Screen a transaction

Submit a payment, withdrawal or crypto transfer to [transaction monitoring](https://docs.didit.me/transaction-monitoring/overview.md); the response carries its status and risk score.

```go theme={null}
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/didit-protocol/sdk-go"
)

func main() {
	client := didit.NewClient()

	transaction, err := client.Transactions.Create(context.Background(), didit.TransactionsCreateParams{
		TransactionID:       "txn-1001",
		TransactionCategory: "finance",
		TransactionDetails: didit.TransactionsCreateParamsTransactionDetails{
			Direction: "outbound",
			Amount:    1500,
			Currency:  "EUR",
		},
		Subject: didit.TransactionsCreateParamsSubject{
			EntityType: didit.String("individual"),
			VendorData: "user-123",
			FullName:   "Maria Garcia",
		},
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(transaction.Status, transaction.Score)
}
```

## Verify Authentication tokens

The `authentication` package checks the access tokens your [Authentication](https://docs.didit.me/authentication/overview.md) issuer signs, in your backend, without calling Didit for each request. It also exchanges codes, refreshes tokens, checks and revokes sessions, and runs step-ups.

> [!NOTE]
>
> Token verification makes the checks in [Verify a token on your server](https://docs.didit.me/authentication/tokens-and-jwks.md#verify-a-token-on-your-server), against your application's issuer and signing keys.

```go theme={null}
package main

import (
	"context"
	"errors"
	"fmt"
	"log"

	"github.com/didit-protocol/sdk-go"
	"github.com/didit-protocol/sdk-go/authentication"
)

func main() {
	// The publishable key (pk_live_... or pk_test_...) carries your issuer URL.
	auth, err := authentication.NewFromPublishableKey("pk_live_...", authentication.WithClientID("your-application-id"))
	if err != nil {
		log.Fatal(err)
	}

	claims, err := auth.VerifyAccessToken(context.Background(), "the bearer token")
	var invalid *didit.TokenVerificationError
	if errors.As(err, &invalid) {
		fmt.Println("rejected:", invalid.Reason)
		return
	} else if err != nil {
		log.Fatal(err)
	}
	fmt.Println(claims.Subject)
}
```

## Errors

```go theme={null}
package main

import (
	"context"
	"errors"
	"fmt"

	"github.com/didit-protocol/sdk-go"
)

func main() {
	client := didit.NewClient()

	_, err := client.Sessions.RetrieveDecision(context.Background(), "11111111-2222-3333-4444-555555555555",
		didit.SessionsRetrieveDecisionParams{})
	var notFound *didit.NotFoundError
	var status *didit.APIStatusError
	switch {
	case errors.As(err, &notFound):
		fmt.Println("no such session")
	case errors.As(err, &status):
		fmt.Println(status.StatusCode, status.RequestID, status.Body)
	case err != nil:
		fmt.Println("no response:", err)
	}
}
```

## Lists and files

```go theme={null}
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/didit-protocol/sdk-go"
)

func main() {
	ctx := context.Background()
	client := didit.NewClient()

	// Lists page through every result.
	iter := client.Sessions.ListAutoPaging(ctx, didit.SessionsListParams{Status: didit.String("Approved")})
	for iter.Next() {
		fmt.Println(iter.Current().SessionID)
	}
	if err := iter.Err(); err != nil {
		log.Fatal(err)
	}

	// Uploads take didit.FileFromPath, FileFromBytes or FileFromReader.
	result, err := client.IDVerification.Verify(ctx, didit.IDVerificationVerifyParams{
		FrontImage: didit.FileFromPath("id-front.jpg"),
		BackImage:  didit.Some(didit.FileFromPath("id-back.jpg")),
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(result.IDVerification.Status)
}
```

## Endpoints not in the Go SDK yet

Go 1.0.1 was generated before these endpoints were added to the API, so it has no method for them and their pages have no Go sample:

* [Validate an address](https://docs.didit.me/standalone-apis/address-validation.md)
* [Batch delete transactions](https://docs.didit.me/management-api/transactions/delete.md)
* Travel Rule networks: [get the networks](https://docs.didit.me/management-api/travel-rule/get-networks.md), [request access to a network](https://docs.didit.me/management-api/travel-rule/request-network.md), and [get](https://docs.didit.me/management-api/travel-rule/get-network-questionnaire.md), [save](https://docs.didit.me/management-api/travel-rule/update-network-questionnaire.md) or [submit](https://docs.didit.me/management-api/travel-rule/submit-network-questionnaire.md) the network questionnaire
* Travel Rule wallet proofs: [list the screenshot wallet proofs](https://docs.didit.me/management-api/travel-rule/list-wallet-proofs.md) and [approve or reject one](https://docs.didit.me/management-api/travel-rule/decide-wallet-proof.md)
* Google Wallet, which no 1.0.1 release has yet: [start a Google Wallet verification](https://docs.didit.me/api-reference/digital-id-wallets/google-wallet.md) and [submit a wallet response](https://docs.didit.me/standalone-apis/digital-id-wallet-response.md)

It also cannot send these newer request fields:

* `biometric_consent`, `tags` and `user_state` when it [creates a session](https://docs.didit.me/sessions-api/create-session.md)
* `biometric_consent_mode` when it [updates the webhook configuration](https://docs.didit.me/management-api/webhook/update-config.md)
* `beneficiary_vasp` when it [updates a Travel Rule transfer](https://docs.didit.me/management-api/travel-rule/update-transfer.md)
* `confirm_delete`, which [deleting a finalized report](https://docs.didit.me/management-api/regulatory-reports/delete.md) needs
* the 16 newer Database Validation fields that no 1.0.1 release has

Until a later Go release adds them, call these over HTTP as each page's cURL request shows. Every other server endpoint page of the [API reference](https://docs.didit.me/api-reference/overview.md) has a Go sample for its operation.


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