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

# SDKs

> Official Retab client libraries.

Every Retab endpoint is available through a typed client library. The SDKs are open-source under [`retab-dev/retab`](https://github.com/retab-dev/retab).

## Install

<CodeGroup>
  ```sh Python theme={null}
  pip install retab
  ```

  ```sh Node theme={null}
  npm install @retab/node
  ```

  ```sh Go theme={null}
  go get github.com/retab-dev/retab/clients/go
  ```

  ```sh Ruby theme={null}
  gem install retab
  ```

  ```sh PHP theme={null}
  composer require retab/retab
  ```

  ```sh DotNet theme={null}
  dotnet add package Retab
  ```

  ```sh Rust theme={null}
  cargo add retab
  ```

  ```sh Java theme={null}
  # Maven
  mvn dependency:get -Dartifact=com.retab:retab:0.0.12

  # Gradle
  gradle dependencies --configuration implementation --dependency com.retab:retab:0.0.12
  ```
</CodeGroup>

## Authenticate

Each SDK reads `RETAB_API_KEY` from the environment by default. Get a key from the [dashboard settings](https://retab.com/dashboard/settings).

Use `RETAB_API_KEY=rt_test_...` for development and CI, and
`RETAB_API_KEY=rt_live_...` for production. The key prefix selects the
[customer environment](/environments/overview) — `RETAB_BASE_URL` is the
Retab deployment URL, not the environment selector. Legacy `sk_retab_...`
keys still work and resolve to production.

```sh theme={null}
# Development / CI
export RETAB_API_KEY=rt_test_…

# Production
export RETAB_API_KEY=rt_live_…
```

<CodeGroup>
  ```python Python theme={null}
  from retab import Retab

  client = Retab() # uses RETAB_API_KEY

  ```

  ```typescript TypeScript theme={null}
  import { Retab } from "@retab/node";

  const client = new Retab({ apiKey: process.env.RETAB_API_KEY });
  ```

  ```go Go theme={null}
  import retab "github.com/retab-dev/retab/clients/go"

  client, _ := retab.NewClient("")  // empty string → reads RETAB_API_KEY
  _ = client
  ```

  ```ruby Ruby theme={null}
  require 'retab'

  client = Retab::Client.new(api_key: ENV['RETAB_API_KEY'])
  ```

  ```php PHP theme={null}
  <?php
  require 'vendor/autoload.php';

  use Retab\Client;

  $client = new Client(apiKey: getenv('RETAB_API_KEY'));
  ```

  ```csharp C# theme={null}
  using Retab;
  using RetabClient = Retab.Retab;

  var client = new RetabClient(Environment.GetEnvironmentVariable("RETAB_API_KEY"));
  ```

  ```rust Rust theme={null}
  use retab::Retab;

  let client = Retab::new(std::env::var("RETAB_API_KEY")?);
  ```

  ```java Java theme={null}
  import com.retab.RetabClient;
  import com.retab.workflowevalruns.WorkflowEvalRunsApi;

  public final class Example {
    public static void main(String[] args) {
      RetabClient client = new RetabClient(System.getenv("RETAB_API_KEY"));
      System.out.println(client.workflows() != null);
    }
  }
  ```
</CodeGroup>

For a worked end-to-end extraction, see the [quickstart in the introduction](./introduction). Full method signatures live under [API Reference](../api-reference/introduction).

## Workflow Graph Resources

Blocks and edges are flat workflow resources keyed by `workflow_id`. SDK calls
take the workflow id explicitly instead of nesting graph resources under a
`/workflows/{workflow_id}/...` path.

When creating or updating workflow blocks with a `json_schema` config, the
public API treats the schema structurally. JSON object property order is not
preserved as authored display order; the dashboard may preserve authored order
through internal editor state.

<CodeGroup>
  ```python Python theme={null}
  block = client.workflows.blocks.create(
      workflow_id="wf_abc123",
      id="blk_extract_1",
      type="extract",
      label="Extract invoice",
      config={
          "model": "retab-small",
          "json_schema": {
              "type": "object",
              "properties": {"invoice_number": {"type": "string"}},
              "required": ["invoice_number"],
          },
      },
  )

  edge = client.workflows.edges.create(
      workflow_id="wf_abc123",
      id="edge_start_extract",
      source_block="start",
      target_block=block.id,
  )
  ```

  ```typescript TypeScript theme={null}
  const block = await client.workflows.blocks.create("wf_abc123", "extract", "blk_extract_1", "Extract invoice", undefined, undefined, undefined, undefined, {
      model: "retab-small",
      json_schema: {
        type: "object",
        properties: { invoice_number: { type: "string" } },
        required: ["invoice_number"],
      },
    });

  const edge = await client.workflows.edges.create("wf_abc123", "start", block.id, "edge_start_extract");
  ```

  ```ruby Ruby theme={null}
  require 'retab'

  client = Retab::Client.new(api_key: ENV['RETAB_API_KEY'])

  block = client.workflows.blocks.create(
    workflow_id: 'wf_abc123',
    id: 'blk_extract_1',
    type: 'extract',
    label: 'Extract invoice',
    config: {
      'model' => 'retab-small',
      'json_schema' => {
        'type' => 'object',
        'properties' => { 'invoice_number' => { 'type' => 'string' } },
        'required' => ['invoice_number'],
      },
    },
  )

  edge = client.workflows.edges.create(
    workflow_id: 'wf_abc123',
    id: 'edge_start_extract',
    source_block: 'start',
    target_block: block.id,
  )
  ```

  ```php PHP theme={null}
  <?php
  use Retab\Resource\WorkflowBlockCreateRequestType;

  $block = $client->workflows()->blocks()->create(
      workflowId: 'wf_abc123',
      type: WorkflowBlockCreateRequestType::Extract,
      id: 'blk_extract_1',
      label: 'Extract invoice',
      config: [
          'model' => 'retab-small',
          'json_schema' => [
              'type' => 'object',
              'properties' => ['invoice_number' => ['type' => 'string']],
              'required' => ['invoice_number'],
          ],
      ],
  );

  $edge = $client->workflows()->edges()->create(
      workflowId: 'wf_abc123',
      sourceBlock: 'start',
      targetBlock: $block->id,
      id: 'edge_start_extract',
  );
  ```

  ```csharp C# theme={null}
  using System.Collections.Generic;
  using Retab;
  using RetabClient = Retab.Retab;

  var client = new RetabClient(Environment.GetEnvironmentVariable("RETAB_API_KEY"));

  var block = await client.Workflows.Blocks.CreateAsync(
      new WorkflowBlocksCreateOptions
      {
          WorkflowId = "wf_abc123",
          Id = "blk_extract_1",
          Type = WorkflowBlockCreateRequestType.Extract,
          Label = "Extract invoice",
          Config = new Dictionary<string, object>
          {
              ["model"] = "retab-small",
              ["json_schema"] = new Dictionary<string, object>
              {
                  ["type"] = "object",
                  ["properties"] = new Dictionary<string, object>
                  {
                      ["invoice_number"] = new Dictionary<string, object> { ["type"] = "string" },
                  },
                  ["required"] = new List<string> { "invoice_number" },
              },
          },
      }
  );

  var edge = await client.Workflows.Edges.CreateAsync(
      new WorkflowEdgesCreateOptions
      {
          WorkflowId = "wf_abc123",
          Id = "edge_start_extract",
          SourceBlock = "start",
          TargetBlock = block.Id,
      }
  );
  ```

  ```go Go theme={null}
  package main

  import (
  	"context"
  	"log"

  	retab "github.com/retab-dev/retab/clients/go"
  )

  func main() {
  	client, err := retab.NewClient("")
  	if err != nil {
  		log.Fatal(err)
  	}

  	blockID := "blk_extract_1"
  	label := "Extract invoice"
  	block, err := client.Workflows.Blocks.Create(context.Background(), &retab.WorkflowBlocksCreateParams{
  		WorkflowID: "wf_abc123",
  		ID:         &blockID,
  		Type:       retab.WorkflowBlockCreateRequestTypeExtract,
  		Label:      &label,
  		Config: &map[string]any{
  			"model": "retab-small",
  			"json_schema": map[string]any{
  				"type": "object",
  				"properties": map[string]any{
  					"invoice_number": map[string]any{"type": "string"},
  				},
  			},
  		},
  	})
  	if err != nil {
  		log.Fatal(err)
  	}

  	edgeID := "edge_start_extract"
  	_, err = client.Workflows.Edges.Create(context.Background(), &retab.WorkflowEdgesCreateParams{
  		WorkflowID:   "wf_abc123",
  		ID:           &edgeID,
  		SourceBlock:  "start",
  		TargetBlock:  block.ID,
  	})
  	if err != nil {
  		log.Fatal(err)
  	}
  }
  ```

  ```rust Rust theme={null}
  use retab::{
      enums::WorkflowBlockCreateRequestType,
      models::{WorkflowBlockCreateRequest, WorkflowEdgeCreateRequest},
      resources::{workflow_blocks, workflow_edges},
      Retab,
  };

  let client = Retab::new(std::env::var("RETAB_API_KEY")?);
  let mut block_request = WorkflowBlockCreateRequest::new("wf_abc123", WorkflowBlockCreateRequestType::Extract);
  block_request.id = Some("blk_extract_1".to_string());
  block_request.label = Some("Extract invoice".to_string());
  let block = client
      .workflows().blocks()
      .create(workflow_blocks::CreateParams::new(block_request))
      .await?;

  let mut edge_request = WorkflowEdgeCreateRequest::new("wf_abc123", "start", &block.id);
  edge_request.id = Some("edge_start_extract".to_string());
  client
      .workflows().edges()
      .create(workflow_edges::CreateParams::new(edge_request))
      .await?;
  ```

  ```java Java theme={null}
  import com.retab.RetabClient;
  import com.retab.workflowevalruns.WorkflowEvalRunsApi;

  public final class Example {
    public static void main(String[] args) throws Exception {
      RetabClient client = new RetabClient(System.getenv("RETAB_API_KEY"));

      var result = client.workflows().blocks().create("wf_abc123", null, null, null, null, null, null, null, null, null);
      System.out.println(result);
    }
  }
  ```
</CodeGroup>

## Workflow Reviews

Reviews use immutable versions. To submit a corrected output, create a new
version under `client.workflows.reviews.versions` and pass the reviewed
`parent_id`/`parentVersionId`; SDK-created versions are always corrections of an
existing version.

<CodeGroup>
  ```python Python theme={null}
  review = client.workflows.reviews.get("rev_abc123")
  versions = client.workflows.reviews.versions.list(review_id=review.id)

  corrected = client.workflows.reviews.versions.create(
      review_id=review.id,
      parent_id=versions.data[0].id,
      snapshot={"invoice_number": "INV-1001", "total": 1200},
      note="fixed total",
  )

  decision = client.workflows.reviews.approve(
      review.id,
      version_id=corrected.id,
  )
  ```

  ```typescript TypeScript theme={null}
  const review = await client.workflows.reviews.get("rev_abc123");
  const versions = await client.workflows.reviews.versions.list({
    reviewId: review.id,
  });

  const corrected = await client.workflows.reviews.versions.create(review.id, undefined, { invoice_number: "INV-1001", total: 1200 }, "fixed total");

  const decision = await client.workflows.reviews.approve(review.id, corrected.id);
  ```

  ```ruby Ruby theme={null}
  require 'retab'

  client = Retab::Client.new(api_key: ENV['RETAB_API_KEY'])

  review = client.workflows.reviews.get(review_id: 'rev_abc123')
  versions = client.workflows.reviews.versions.list(review_id: review.id)

  corrected = client.workflows.reviews.versions.create(
    review_id: review.id,
    parent_id: versions.data[0].id,
    snapshot: { 'invoice_number' => 'INV-1001', 'total' => 1200 },
    note: 'fixed total',
  )

  decision = client.workflows.reviews.approve(
    review_id: review.id,
    version_id: corrected.id,
  )
  ```

  ```php PHP theme={null}
  <?php
  $review = $client->workflows()->reviews()->get('rev_abc123');
  $versions = $client->workflows()->reviews()->versions()->list(reviewId: $review->id);

  $corrected = $client->workflows()->reviews()->versions()->create(
      reviewId: $review->id,
      parentId: $versions->data[0]->id,
      snapshot: ['invoice_number' => 'INV-1001', 'total' => 1200],
      note: 'fixed total',
  );

  $decision = $client->workflows()->reviews()->approve(
      $review->id,
      versionId: $corrected->id,
  );
  ```

  ```csharp C# theme={null}
  using System.Collections.Generic;
  using Retab;
  using RetabClient = Retab.Retab;

  var client = new RetabClient(Environment.GetEnvironmentVariable("RETAB_API_KEY"));

  var review = await client.Workflows.Reviews.GetAsync("rev_abc123");
  var versions = await client.Workflows.Reviews.Versions.ListAsync(
      new WorkflowReviewVersionsListOptions { ReviewId = review.Id }
  );

  var corrected = await client.Workflows.Reviews.Versions.CreateAsync(
      new WorkflowReviewVersionsCreateOptions
      {
          ReviewId = review.Id,
          ParentId = versions.Data[0].Id,
          Snapshot = new Dictionary<string, object>
          {
              ["invoice_number"] = "INV-1001",
              ["total"] = 1200,
          },
          Note = "fixed total",
      }
  );

  var decision = await client.Workflows.Reviews.ApproveAsync(
      review.Id,
      new WorkflowReviewsApproveOptions { VersionId = corrected.Id }
  );
  ```

  ```go Go theme={null}
  package main

  import (
  	"context"
  	"log"

  	retab "github.com/retab-dev/retab/clients/go"
  )

  func main() {
  	client, err := retab.NewClient("")
  	if err != nil {
  		log.Fatal(err)
  	}

  	reviewID := "rev_abc123"
  	versions, err := client.Workflows.Reviews.Versions.List(context.Background(), &retab.WorkflowReviewVersionsListParams{ReviewID: reviewID})
  	if err != nil {
  		log.Fatal(err)
  	}
  	note := "fixed total"
  	corrected, err := client.Workflows.Reviews.Versions.Create(context.Background(), &retab.WorkflowReviewVersionsCreateParams{
  		ReviewID: reviewID,
  		ParentID: versions.Data[0].ID,
  		Snapshot: map[string]any{"invoice_number": "INV-1001", "total": 1200},
  		Note:     &note,
  	})
  	if err != nil {
  		log.Fatal(err)
  	}
  	_, err = client.Workflows.Reviews.Approve(context.Background(), reviewID, &retab.WorkflowReviewsApproveParams{VersionID: corrected.ID})
  	if err != nil {
  		log.Fatal(err)
  	}
  }
  ```

  ```rust Rust theme={null}
  use retab::{
      models::{ApproveReviewRequest, CreateReviewVersionRequest},
      resources::{workflow_review_versions, workflow_reviews},
      Retab,
  };
  use std::collections::HashMap;

  let client = Retab::new(std::env::var("RETAB_API_KEY")?);
  let review_id = "rev_abc123";
  let versions = client
      .workflows()
      .reviews()
      .versions()
      .list(workflow_review_versions::ListParams::new(review_id))
      .await?;
  let mut snapshot = HashMap::new();
  snapshot.insert("invoice_number".to_string(), serde_json::json!("INV-1001"));
  snapshot.insert("total".to_string(), serde_json::json!(1200));
  let corrected = client
      .workflows()
      .reviews()
      .versions()
      .create(workflow_review_versions::CreateParams::new(CreateReviewVersionRequest::new(
          review_id,
          &versions.data[0].id,
          snapshot,
      )))
      .await?;
  let decision = client
      .workflows()
      .reviews()
      .approve(
          review_id,
          workflow_reviews::ApproveParams::new(ApproveReviewRequest::new(&corrected.id)),
      )
      .await?;
  ```

  ```java Java theme={null}
  import com.retab.RetabClient;
  import com.retab.workflowevalruns.WorkflowEvalRunsApi;

  public final class Example {
    public static void main(String[] args) throws Exception {
      RetabClient client = new RetabClient(System.getenv("RETAB_API_KEY"));

      var result = client.workflows().reviews().get("review_abc123");
      System.out.println(result);
    }
  }
  ```
</CodeGroup>

## Workflow Eval and Experiment Runs

Workflow runs, workflow-eval runs, and experiment runs all use the same
run-id-first pattern in the SDKs: create a run from the parent resource, poll the
run by its `id`, then inspect child records under that run.

<CodeGroup>
  ```python Python theme={null}
  eval_run = client.workflows.evals.runs.create(
      workflow_id="wf_abc123",
      eval_id="eval_abc",
  )
  eval_run = client.workflows.evals.runs.get(eval_run.id)
  eval_results = client.workflows.evals.runs.results.list(eval_run.id)

  experiment_run = client.workflows.experiments.runs.create(
  workflow_id="wf_abc123",
  experiment_id="exp_abc",
  )
  experiment_run = client.workflows.experiments.runs.get(experiment_run.id)
  experiment_results = client.workflows.experiments.runs.results.list(experiment_run.id)
  metrics = client.workflows.experiments.runs.metrics.get(
  experiment_run.id,
  view="summary",
  )

  ```

  ```typescript TypeScript theme={null}
  const evalRun = await client.workflows.evals.runs.create({
    workflowId: "wf_abc123",
    evalId: "eval_abc",
  });
  const currentEvalRun = await client.workflows.evals.runs.get(evalRun.id);
  const evalResults = await client.workflows.evals.runs.results.list(evalRun.id);

  const experimentRun = await client.workflows.experiments.runs.create("exp_abc", "wf_abc123");
  const currentExperimentRun = await client.workflows.experiments.runs.get(experimentRun.id);
  const experimentResults = await client.workflows.experiments.runs.results.list(experimentRun.id);
  const metrics = await client.workflows.experiments.runs.metrics.get({
    runId: experimentRun.id,
    view: "summary",
  });
  ```

  ```ruby Ruby theme={null}
  require 'retab'

  client = Retab::Client.new(api_key: ENV['RETAB_API_KEY'])

  eval_run = client.workflows.evals.runs.create(
    workflow_id: 'wf_abc123',
    eval_id: 'eval_abc',
  )
  eval_run = client.workflows.evals.runs.get(run_id: eval_run.id)
  eval_results = client.workflows.evals.runs.results.list(run_id: eval_run.id)

  experiment_run = client.workflows.experiments.runs.create(
    workflow_id: 'wf_abc123',
    experiment_id: 'exp_abc',
  )
  experiment_run = client.workflows.experiments.runs.get(run_id: experiment_run.id)
  experiment_results = client.workflows.experiments.runs.results.list(run_id: experiment_run.id)
  metrics = client.workflows.experiments.runs.metrics.get(
    run_id: experiment_run.id,
    view: 'summary',
  )
  ```

  ```php PHP theme={null}
  <?php
  use Retab\Resource\ExperimentRunMetricsView;

  $evalRun = $client->workflows()->evals()->runs()->create(
      workflowId: 'wf_abc123',
      evalId: 'eval_abc',
  );
  $evalRun = $client->workflows()->evals()->runs()->get($evalRun->id);
  $evalResults = $client->workflows()->evals()->results()->list(runId: $evalRun->id);

  $experimentRun = $client->workflows()->experiments()->runs()->create(
      experimentId: 'exp_abc',
      workflowId: 'wf_abc123',
  );
  $experimentRun = $client->workflows()->experiments()->runs()->get($experimentRun->id);
  $experimentResults = $client->workflows()->experiments()->results()->list(runId: $experimentRun->id);
  $metrics = $client->workflows()->experiments()->metrics()->get(
      runId: $experimentRun->id,
      view: ExperimentRunMetricsView::Summary,
  );
  ```

  ```csharp C# theme={null}
  using Retab;
  using RetabClient = Retab.Retab;

  var client = new RetabClient(Environment.GetEnvironmentVariable("RETAB_API_KEY"));

  var evalRun = await client.Workflows.Evals.Runs.CreateAsync(
      new WorkflowEvalRunsCreateOptions
      {
          WorkflowId = "wf_abc123",
          Scope = new WorkflowEvalRunSingleScope { EvalId = "eval_abc" },
      }
  );
  evalRun = await client.Workflows.Evals.Runs.GetAsync(evalRun.Id);
  var evalResults = await client.Workflows.Evals.Results.ListAsync(
      new WorkflowEvalRunResultsListOptions { RunId = evalRun.Id }
  );

  var experimentRun = await client.Workflows.Experiments.Runs.CreateAsync(
      new ExperimentRunsCreateOptions
      {
          WorkflowId = "wf_abc123",
          ExperimentId = "exp_abc",
      }
  );
  experimentRun = await client.Workflows.Experiments.Runs.GetAsync(experimentRun.Id);
  var experimentResults = await client.Workflows.Experiments.Results.ListAsync(
      new ExperimentRunResultsListOptions { RunId = experimentRun.Id }
  );
  var metrics = await client.Workflows.Experiments.Metrics.GetAsync(
      new ExperimentRunMetricsGetOptions { RunId = experimentRun.Id }
  );
  ```

  ```go Go theme={null}
  package main

  import (
  	"context"
  	"fmt"
  	"log"

  	retab "github.com/retab-dev/retab/clients/go"
  )

  func main() {
  	client, err := retab.NewClient("")
  	if err != nil {
  		log.Fatal(err)
  	}

  	evalID := "eval_abc"
  	scope := retab.WorkflowEvalRunScope{
  		Type:   retab.WorkflowEvalRunScopeTypeSingle,
  		EvalID: &evalID,
  	}
  	evalRun, err := client.Workflows.Evals.Runs.Create(context.Background(), &retab.WorkflowEvalRunsCreateParams{
  		WorkflowID: "wf_abc123",
  		Scope:      &scope,
  	})
  	if err != nil {
  		log.Fatal(err)
  	}
  	evalResults, err := client.Workflows.Evals.Results.List(context.Background(), &retab.WorkflowEvalRunResultsListParams{RunID: evalRun.ID})
  	if err != nil {
  		log.Fatal(err)
  	}

  	workflowID := "wf_abc123"
  	experimentRun, err := client.Workflows.Experiments.Runs.Create(context.Background(), &retab.ExperimentRunsCreateParams{
  		WorkflowID:    &workflowID,
  		ExperimentID: "exp_abc",
  	})
  	if err != nil {
  		log.Fatal(err)
  	}
  	metrics, err := client.Workflows.Experiments.Metrics.Get(context.Background(), &retab.ExperimentRunMetricsGetParams{RunID: experimentRun.ID})
  	if err != nil {
  		log.Fatal(err)
  	}

  	fmt.Println(evalResults, metrics)
  }
  ```

  ```rust Rust theme={null}
  use retab::{
      models::{CreateExperimentRunRequest, CreateWorkflowEvalRunRequest},
      resources::{experiment_run_metrics, experiment_runs, workflow_eval_run_results, workflow_eval_runs},
      Retab,
  };

  let client = Retab::new(std::env::var("RETAB_API_KEY")?);
  let eval_run = client
      .workflows()
      .evals()
      .runs()
      .create(workflow_eval_runs::CreateParams::new(
          CreateWorkflowEvalRunRequest::new("wf_abc"),
      ))
      .await?;
  let eval_results = client
      .workflows()
      .evals()
      .results()
      .list(workflow_eval_run_results::ListParams::new(&eval_run.id))
      .await?;
  let experiment_run = client
      .workflows()
      .experiments()
      .runs()
      .create(experiment_runs::CreateParams::new(CreateExperimentRunRequest::new("exp_abc")))
      .await?;
  let metrics = client
      .workflows()
      .experiments()
      .metrics()
      .get(experiment_run_metrics::GetParams::new(&experiment_run.id))
      .await?;

  println!("{eval_results:#?} {metrics:#?}");
  ```

  ```java Java theme={null}
  import com.retab.RetabClient;
  import com.retab.workflowevalruns.WorkflowEvalRunsApi;

  public final class Example {
    public static void main(String[] args) throws Exception {
      RetabClient client = new RetabClient(System.getenv("RETAB_API_KEY"));

      var result = new WorkflowEvalRunsApi(client).create("wf_abc123", null);
      System.out.println(result);
    }
  }
  ```
</CodeGroup>

These surfaces do not expose public `job_id` or `batch_id` handles. Use the
returned run `id` for polling, cancellation, results, and experiment metrics.
