Give stations a tool from an HTTP API

The agent gets a command that calls the API, and Spotlight keeps the API key, which the agent never sees. You need:

  • Terraform set up with the Spotlight provider, using a token that has the configure and secrets scopes. See Manage Spotlight with Terraform.
  • A key for the API.
  • A Spotlight deployment that allows outbound calls to the API's host, since Spotlight calls the API from its own servers.

If the API publishes an OpenAPI description or a GraphQL schema, see Generate tools from an API description instead.

The example uses a placeholder API at api.acme.example that takes a comment as a JSON POST to /comments. Put your own API's host, path and body in its place.

Keep the API key as a secret

Bind the secret to the API's host, in the header and with the prefix the API expects. Spotlight sends it to that host only, over HTTPS.

variable "acme_api_key" {
  type      = string
  sensitive = true
}

data "spotlight_workspace" "acme" {
  slug = "acme"
}

resource "spotlight_secret" "acme_api_key" {
  workspace_id = data.spotlight_workspace.acme.id
  name         = "ACME_API_KEY"
  bindings = [
    { host = "api.acme.example", carrier = "header", name = "authorization", prefix = "Bearer " },
  ]
  value_wo         = var.acme_api_key
  value_wo_version = 1
}

To rotate the key later, give the new value and raise value_wo_version.

If the API reads the key from the query

Some APIs, such as Google Maps, take the key as a query parameter. Bind the secret with carrier = "query" and the parameter's name:

resource "spotlight_secret" "maps_key" {
  workspace_id = data.spotlight_workspace.acme.id
  name         = "MAPS_KEY"
  bindings = [
    { host = "maps.googleapis.com", carrier = "query", name = "key" },
  ]
  value_wo         = var.maps_key
  value_wo_version = 1
}

Leave the key out of the request's url and name only the secret, as in auth = { secrets = ["secret.MAPS_KEY"] }. For how Spotlight sets the parameter and keeps the key out of what it records, see Bindings.

Define the action and the tool

Write the request as an action, and offer it to agents as a tool: does names the action, description tells the agent when to use it, and each input becomes a flag. In the request, read the inputs as inputs.<name> and name the secret in auth.secrets.

data "spotlight_team" "core" {
  workspace_id = data.spotlight_workspace.acme.id
  key          = "core"
}

resource "spotlight_integration" "acme" {
  team_id = data.spotlight_team.core.id
  definition = {
    integration = "acme"
    actions = {
      comment = {
        request = {
          method = "POST"
          url    = "https://api.acme.example/comments"
          body = {
            issue = "$${{ inputs.issue }}"
            text  = "$${{ inputs.body }}"
          }
          auth = { secrets = ["secret.${spotlight_secret.acme_api_key.name}"] }
        }
      }
    }
    tools = {
      comment = {
        does        = "comment"
        description = "Comment on the issue."
        inputs = {
          issue = { type = "string", required = true }
          body  = { type = "string", required = true }
        }
      }
    }
  }
}

An input is text unless you say otherwise. For a number, give it type = "integer" or type = "number", and for a flag that is on or off, type = "boolean". For a list or an object, give its JSON Schema as schema in place of type; the agent then passes it as JSON:

labels = { schema = { type = "array", items = { type = "string" } } }

If the API names a field team_id or teamId, name the input the same way: an input name may hold letters, digits, _ and -.

Give the tool to a station

Add the tool to the station's tools in the line. Name it through the integration's tools map, so renaming or removing the tool fails the plan here:

station {
  key       = "implementation"
  name      = "Implementation"
  harness   = "claude-code"
  tools     = [spotlight_integration.acme.tools["comment"]]
  passes_to = ["code-review"]
}

Outside Terraform, the name is <integration>.<tool>, as in acme.comment.

Run terraform apply. The next run at that station has the tool. Its agent calls it as a command:

spotlight acme comment --issue ACME-42 --body "Fixed in the linked pull request."

spotlight tools lists the tools a run was given, with their flags. spotlight tools describe acme.comment prints one with its inputs and an example call.

See what a tool answers with

After a call to the tool succeeds, Spotlight learns the shape of its answer: the field names and their types, never the values. In runs that start after that call, spotlight tools describe prints it under Answers with (learned from earlier calls, may be incomplete):, so the agent knows which fields to read. Each later successful call adds to it. Redefining or removing the integration, and disbanding the team, forgets what was learned. See Learned output.

If a person should agree before each call, see Require approval before a tool runs.

On this page