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

# Connect an Internal System

> Turn an internal HTTP service or a read-only database into governed tools without writing code

The Connector Builder is the advanced path. The usual way to bring an internal system to your agents is [Connect a system](/organizations/internal-tools): one base URL, connection reference or MCP URL, discovery, a governed generic surface at once, and named tools promoted from real calls. Reach for the builder when you want to write the tools yourself, endpoint by endpoint or template by template, with parameters, output schemas and a dry run.

The builder describes an internal system as data, and Danube compiles it into a private, organization-scoped service whose tools behave like any other: searchable by your agents, credential-injected at call time, covered by [policies](/organizations/policies) and the [audit log](/organizations/audit-log).

Two kinds of connector exist.

## HTTP connector

For an internal HTTP service with no published OpenAPI document. You describe the base URL, how it authenticates, and each endpoint you want exposed as a tool.

```json theme={null}
{
  "name": "Billing Service",
  "description": "Internal invoicing service",
  "base_url": "https://billing.internal.example.com",
  "auth": {"type": "api_key", "location": "header", "name": "X-API-Key"},
  "endpoints": [
    {
      "name": "Get Invoice",
      "description": "Fetch one invoice by id",
      "method": "GET",
      "path": "/v1/invoices/{id}",
      "parameters": [
        {"name": "id", "location": "path", "type": "string", "required": true}
      ]
    }
  ],
  "visibility": "private"
}
```

| Field | Notes |
| - | - |
| `auth.type` | `none`, `api_key` (header or query, with `name`), `bearer`, `basic`, `aws_sigv4`, `oauth2_client_credentials` or `mtls` (see [Authentication methods](#authentication-methods)). Compiled to the same credential-injection path every other service uses; the secret itself is stored as a credential, never in the spec. |
| `endpoints[].path` | Appended to `base_url`; `{placeholders}` become path parameters. |
| `endpoints[].parameters[].location` | `query`, `path`, `header`, or `body`. |
| `endpoints[].body_type` | `json` (default), `form` (`application/x-www-form-urlencoded`) or `graphql` (see [GraphQL endpoints](#graphql-endpoints)). |
| `endpoints[].output_schema` | Optional; the dry run checks sample responses against it. |
| `environments`, `active_environment` | Optional map of environment name to base URL, for promoting a connector from staging to production without editing endpoints. |
| `visibility` | `private` by default (your organization only). |

### Authentication methods

Every method keeps its secret out of the spec: the spec names the method and its non-secret settings, and the secret is entered once as the connector's credential (personal or shared with the organization). A connector that runs on the [data plane](/organizations/data-plane) stores each secret as an `env://` or `vault://` reference that the agent resolves inside your network; the agent signs, mints tokens and presents certificates itself, from the same tool definition Danube uses centrally.

| `auth.type` | Spec fields | Credential fields | Notes |
| - | - | - | - |
| `aws_sigv4` | `aws_service`, `aws_region` | `access_key_id`, `secret_access_key`, optional `session_token` (temporary credentials), optional `region` (overrides the spec) | Signs every request with AWS Signature Version 4. Use the service name AWS signs for: `s3` for S3, `athena` for Athena, `redshift-data` for the Redshift Data API, `lambda` for Lambda function URLs and the Lambda API, `execute-api` for an API Gateway endpoint with IAM authorization. S3 bodies over 8 MB are sent with `UNSIGNED-PAYLOAD`. |
| `oauth2_client_credentials` | `token_url` (https), optional `scope`, optional `audience`, `client_auth` (`basic`, the default, or `body`) | `client_id`, `client_secret` | The client-credentials grant: a token is minted from `token_url`, cached until it expires, sent as `Authorization: Bearer`, and minted again once if the service answers 401. This is how an API behind Okta or Microsoft Entra is called by a registered application, and how a Google service account with a client id and secret is used. `audience` is for servers that require it (Auth0, an Okta custom authorization server). |
| `mtls` | optional `ca_bundle_ref` (an `env://` or `vault://` reference resolved on the data plane) | `client_cert`, `client_key`, optional `ca_bundle` (all PEM, pasted with their line breaks) | Presents the client certificate on the TLS handshake. The PEMs are written to a private temporary file for the length of one call and removed afterwards, centrally and on the agent alike. Give `ca_bundle` or `ca_bundle_ref` when the service's certificate is issued by your own authority. |

Spec example for an S3 bucket read through an internal proxy:

```json theme={null}
{
  "name": "Reports bucket",
  "base_url": "https://reports-bucket.s3.us-east-1.amazonaws.com",
  "auth": {"type": "aws_sigv4", "aws_service": "s3", "aws_region": "us-east-1"},
  "endpoints": [
    {"name": "Get report", "method": "GET", "path": "/{key}",
     "parameters": [{"name": "key", "location": "path", "type": "string", "required": true}]}
  ]
}
```

And for an API behind Okta:

```json theme={null}
{
  "auth": {
    "type": "oauth2_client_credentials",
    "token_url": "https://acme.okta.com/oauth2/default/v1/token",
    "scope": "inventory.read",
    "client_auth": "basic"
  }
}
```

### GraphQL endpoints

An endpoint with `body_type: "graphql"` compiles to one `POST` tool per operation. `graphql_query` holds the operation; its variables are the endpoint's body-location parameters, and validation warns about a variable with no parameter or a parameter that is not a variable. Danube sends `{"query": ..., "variables": {...}}` as JSON and returns the response's `data` as the result. A response carrying `errors[]`, on HTTP 200 as GraphQL servers answer, is a failed call with `error_type: bad_request`, `fault: caller` and the messages in `error`; `data` is kept as the result when the server returned a partial one.

```json theme={null}
{
  "name": "Customer",
  "path": "/graphql",
  "body_type": "graphql",
  "graphql_query": "query Customer($id: ID!) { customer(id: $id) { id name plan } }",
  "parameters": [{"name": "id", "location": "body", "type": "string", "required": true}]
}
```

### Flow

<Steps>
  <Step title="Open the builder">
    Go to [Dashboard > Tools > Connect internal system](https://danubeai.com/dashboard/tools/connector).
  </Step>

  <Step title="Infer or write the spec">
    If the service publishes an OpenAPI document, point the builder at the base URL and it proposes a spec. Otherwise list the endpoints to probe; the builder calls each one with your sample parameters and proposes parameters and output shapes from the responses. Nothing is stored at this step; you review and edit the proposal.
  </Step>

  <Step title="Validate and test">
    Validation checks the spec shape and reports the number of tools it will create. Test makes one real call to confirm reachability and authentication.
  </Step>

  <Step title="Dry run">
    The dry run executes each endpoint with sample parameters and reports pass, fail, or skip per check, including output-schema mismatches.
  </Step>

  <Step title="Create">
    The connector becomes a private service in your organization. Its tools are immediately callable by your agents.
  </Step>
</Steps>

## Database connector

For read-only questions against Postgres, Redshift, MySQL, Snowflake, BigQuery, Databricks, ClickHouse or Trino. Each query becomes a tool; parameters are typed and bound, never interpolated. On the hosted service a database connector belongs to an organization and runs through its data-plane agent: see [Connect a Warehouse](/organizations/connect-a-warehouse) for the reference forms, the read-only role per engine and the agent image drivers.

```json theme={null}
{
  "name": "Analytics DB",
  "db_type": "postgres",
  "connection_ref": "env://ANALYTICS_DB_DSN",
  "queries": [
    {
      "name": "Active customers",
      "description": "Customers with activity in the last N days",
      "sql": "SELECT id, name, last_seen FROM customers WHERE last_seen > now() - (:days || ' days')::interval LIMIT 100",
      "parameters": [{"name": "days", "type": "integer", "required": true}]
    }
  ]
}
```

* **Danube never holds the connection string.** `connection_ref` is a reference (`env://NAME`, `vault://<mount>/<path>#<field>`) resolved where the query runs. With the [data plane](/organizations/data-plane) that is inside your network; a connection string in that field is refused.
* **Read-only, enforced three ways.** The SQL is validated as a single `SELECT`, the connection is opened read-only where the engine supports it (a read-only role covers the rest), and results are capped at 1,000 rows with a statement timeout.
* **Introspection** lists tables and columns so the builder can propose a starting set of queries.

## Endpoints

| Method | Path | Purpose |
| - | - | - |
| `POST` | `/v1/connectors/validate` | Check an HTTP spec |
| `POST` | `/v1/connectors/test` | One live call |
| `POST` | `/v1/connectors/infer` | Propose a spec from OpenAPI or probes |
| `POST` | `/v1/connectors/dry-run` | Execute every endpoint with samples |
| `POST` | `/v1/connectors` | Create the service and tools |
| `POST` | `/v1/connectors/database/validate`, `/test`, `/introspect`, `/infer`, `/dry-run` | The same flow for a database |
| `POST` | `/v1/connectors/database` | Create the database service |


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