김인턴
API

API overview

A token belongs to a person, and every call runs as that person.

Every tool internkim uses inside a company is callable from outside it, over HTTP, at https://api.intern.kim/v1. Every company calls that one address; the token is what says which company, and which person inside it. A token belongs to a person and every call runs as that person, so a token can never do more than its owner can.

One process terminates every /api/v1 request: admind. It authenticates the token, checks the scope, and forwards the work — tool calls to capabilityd over a unix socket, agent messages to the Blueclaw runtime over local HTTP. Nothing under /api/v1 executes a tool itself.

Getting a token

Tokens are issued from a signed-in staff web session, either through the token section on the Admin page or with the session cookie directly:

curl -X POST https://api.intern.kim/v1/tokens \
  --cookie "$SESSION" \
  --header 'Content-Type: application/json' \
  --data '{"label":"reporting script","scopes":["write"]}'
{
  "token": "ik_...",
  "actor": {
    "personID": "...",
    "email": "[email protected]",
    "displayName": "이샘플",
    "source": "public_api_token",
    "scopes": ["read", "write"]
  },
  "record": {
    "id": "tok_...",
    "label": "reporting script",
    "email": "[email protected]",
    "scopes": ["read", "write"],
    "createdAt": "2026-08-27T00:00:00Z"
  }
}

token is returned exactly once. The server keeps only a SHA-256 hash of it, in api-tokens.json under admind's state directory (/root/.internkim/state/admin by default). The owner is whoever the signed-in session belongs to; there is no way to issue a token for someone else.

Authentication

Every other endpoint takes the token as a bearer header:

curl https://api.intern.kim/v1/tools \
  --header "Authorization: Bearer $INTERNKIM_TOKEN"

The owner is re-resolved on every request. When the owner stops being active staff, every call with their token answers 403 from that moment on; nothing needs to be cleaned up first.

Scopes

read is always added to a token's scopes, whether requested or not. Requested scopes are lowercased, deduplicated, and anything unknown is silently dropped.

Authorization compares ranks, not scope names. A token's rank is the highest rank among its scopes; a tool's required rank comes from its sideEffectClass.

RankScopesOpens
1readtools whose sideEffectClass is read
2write, external_write, external_send, publish, connect, companion, agent.runworkspace writes, external edits and sends, publishing, connections, browser actions, and the agent endpoints
3destructive, admineverything, including deletes

Three consequences worth knowing:

  • Any rank-2 scope opens every rank-2 tool. A token issued with only connect can call message_send.
  • admin grants nothing beyond what destructive grants.
  • A descriptor with a sideEffectClass the gateway does not recognize requires rank 3. Unknown classes fail closed.

Errors

Success bodies are JSON. Error bodies are plain text.

StatusBodyWhen
400decode error, message is required, conversationID is requiredthe body is not valid JSON, or a required field is missing
401bearer token requiredno Authorization: Bearer header
401invalid bearer tokenthe token is unknown or revoked
403staff web session requiredPOST /api/v1/tokens without a signed-in staff session
403token owner is not active staffthe owner left or was deactivated
403tool is not available for this tokenthe token's rank is below what the tool requires
403write scope requiredan agent endpoint with a rank-1 token
404unknown path, unknown tool, or a tool the token cannot see
500the token record could not be written
502capabilityd or the agent runtime failed; the body carries the upstream error

Revoking a token

Every lookup skips records whose revokedAt is set, so revocation takes effect on the next request. No endpoint sets it yet: to revoke a token today, set revokedAt on its record in api-tokens.json and save the file.

The served reference

/api-docs on the same host serves an interactive reference, in Korean or English, rendered from an OpenAPI document at /openapi/<language>.json. The twenty-six base tools in that document are read from the same catalog the agent runs on.

Where it is implemented

ConcernWhere
Routing for everything under /api/v1handlePublicAPI in internal/admind/public_tool_gateway.go, mounted in internal/admind/service.go
Token issue, ik_ format, SHA-256 hashing, storeissuePublicAPIToken, publicAPITokenHash in the same file; store path from publicAPITokenStorePath
Scope normalisation and ranksnormalizePublicAPITokenScopes, publicAPIScopeRank, publicToolScopeForDescriptor, publicToolAllowedForActor
Forwarding tool calls to capabilitydinvokeCapabilityTool, over the unix socket named by CapabilitySocketPath
Agent endpointsinternal/admind/agent_drive.go, forwarding to Blueclaw through blueclawJSONRequest
Wire typespkg/capabilityprotocol/protocol.go
Tool catalogpkg/capabilityprotocol/generated/capability-tools.json
The contract, stated as testsinternal/admind/public_tool_gateway_test.go
OpenAPI document and /api-docs shellweb/src/lib/server/openapi.ts, web/src/routes/openapi/[language].json/, web/src/routes/api-docs/

On this page