Tyk Agent Onboarding (One-Shot Credential)
Tyk Agent Onboarding — the shortest adapter in the field. Tyk collapses identity + credential + scope + rate-limit into a single POST /tyk/keys call: the addKey operation accepts an access_rights array (which APIs + versions + URL patterns + methods the key can call), rate + per (RPS + window), quota_max + quota_renewal_rate (period quota), and tags, all atomically. There is no separate ensure-consumer-group / ensure-usage-plan / ensure-api-product step; the scope is encoded on the key itself. Runtime policy enforcement (signature verify, consent check, scope classify, audit emit) lives in the orchestration.steps below — each step that gates issuance carries on_failure: deny. Lint-time validation of this capability shape lives in the companion Polychro ruleset at https://github.com/api-evangelist/posts/blob/main/polychro/agent-onboarding-rules.yaml — Polychro is Naftiko's governance layer, separate from the capability spec, and is the correct home for cross-object consistency rules that apply across every agent-onboarding capability.
What You Can Do
MCP Tools
agent-register
agent-revoke
Capability Spec
naftiko: 1.0.0-alpha2
info:
label: Tyk Agent Onboarding (One-Shot Credential)
description: 'Tyk Agent Onboarding — the shortest adapter in the field. Tyk collapses
identity + credential + scope + rate-limit into a single POST /tyk/keys call: the
addKey operation accepts an access_rights array (which APIs + versions + URL patterns
+ methods the key can call), rate + per (RPS + window), quota_max + quota_renewal_rate
(period quota), and tags, all atomically. There is no separate ensure-consumer-group
/ ensure-usage-plan / ensure-api-product step; the scope is encoded on the key itself. Runtime policy enforcement (signature verify, consent check, scope classify, audit emit) lives in the orchestration.steps below — each step that gates issuance carries on_failure: deny. Lint-time validation of this capability shape lives in the companion Polychro ruleset at https://github.com/api-evangelist/posts/blob/main/polychro/agent-onboarding-rules.yaml — Polychro is Naftiko''s governance layer, separate from the capability spec, and is the correct home for cross-object consistency rules that apply across every agent-onboarding capability.'
tags:
- Tyk
- Agent Onboarding
- Web Bot Auth
- GraphQL Gateway
- One-Shot Credential
- Atomic Issuance
- Open Source
- Naftiko Capability
created: '2026-05-28'
modified: '2026-05-28'
related:
- https://apievangelist.com/2026/05/27/automated-agent-onboarding-is-a-naftiko-capability-not-a-gateway-feature/
- https://github.com/api-evangelist/tyk
binds:
- namespace: env
keys:
TYK_GATEWAY_BASE_URI: TYK_GATEWAY_BASE_URI
TYK_GATEWAY_SECRET: TYK_GATEWAY_SECRET
TYK_ANALYTICS_WEBHOOK: TYK_ANALYTICS_WEBHOOK
AGENT_TRUSTED_ISSUERS: AGENT_TRUSTED_ISSUERS
AGENT_CONSENT_HASH: AGENT_CONSENT_HASH
capability:
consumes:
- type: http
namespace: tyk-gateway
baseUri: '{{env.TYK_GATEWAY_BASE_URI}}'
description: Tyk Gateway Admin API — addKey is the one operation that does everything
most other gateways need 3-5 operations for. listKeys + getKey are observation; deleteKey
is revocation.
resources:
- name: keys
path: /tyk/keys
operations:
- name: addkey
method: POST
description: 'Mint an API key with embedded access_rights (the scope), rate + per
(the throttle), quota_max + quota_renewal_rate (the quota), alias (operator-readable
identifier), and tags (the audit join key). Atomic — no enabled-but-unscoped window.
GraphQL gateways: access_rights can declare GraphQL field-level scope under
allowed_urls + restricted_types.'
outputRawFormat: json
outputParameters:
- { name: result, type: object, value: $. }
inputParameters:
- { name: body, in: body, type: object, required: true, description: 'Full SessionState envelope including access_rights + rate + quota.' }
- name: keys-id
path: /tyk/keys/{keyID}
operations:
- name: getkey
method: GET
description: Read a key's current state (for revocation lookups + audit observation).
outputRawFormat: json
outputParameters:
- { name: result, type: object, value: $. }
- name: deletekey
method: DELETE
description: Revoke the key. Atomic — no cascading entity cleanup needed.
outputRawFormat: json
outputParameters:
- { name: result, type: object, value: $. }
- name: apis
path: /tyk/apis
operations:
- name: listapis
method: GET
description: Enumerate APIs registered with the gateway — used during scope
classification to validate that requested api_ids exist before key issuance.
outputRawFormat: json
outputParameters:
- { name: result, type: object, value: $. }
authentication:
type: apikey
key: x-tyk-authorization
value: '{{env.TYK_GATEWAY_SECRET}}'
placement: header
- type: http
namespace: tyk-audit
baseUri: '{{env.TYK_ANALYTICS_WEBHOOK}}'
description: Tyk's OpenAPI exposes no audit operations natively. The orchestration emits
to a customer-configured analytics webhook (Tyk Pump destination, Tyk Dashboard
analytics endpoint, or a generic provider-managed log sink). This is the multi-origin
audit shape the post calls out — analogous to AWS CloudTrail or Azure Monitor.
resources:
- name: audit-events
path: /
operations:
- name: emitevent
method: POST
description: Write a structured agent-onboarding audit event to the customer's
analytics destination.
outputRawFormat: json
outputParameters:
- { name: result, type: object, value: $. }
inputParameters:
- { name: body, in: body, type: object, required: true }
authentication:
type: none
orchestration:
- name: onboard-agent
description: Five-step orchestration — shorter than every other adapter in the series
because Tyk's addKey collapses identity + credential + scope + rate-limit into one
atomic operation. No ensure-scope-tier step. No bind-key-to-tier step. No rollback
window between issuance and binding.
inputs:
- { name: signature, type: object, required: true }
- { name: signature_agent, type: string, required: true }
- { name: skill_id, type: string, required: true }
- { name: requested_scopes, type: array, required: true, description: 'Each scope name maps to a Tyk access_rights array (api_id + versions + allowed_urls + allowed_methods) per provider policy. GraphQL gateways may include restricted_types for field-level scope.' }
- { name: consent_hash, type: string, required: true }
- { name: contact, type: object, required: true }
steps:
- id: verify_signature
type: builtin.web-bot-auth.verify
with:
signature: '${input.signature}'
agent: '${input.signature_agent}'
trusted_issuers: '{{env.AGENT_TRUSTED_ISSUERS}}'
on_failure: deny
- id: verify_consent
type: builtin.policy.assert
with:
assert: '${input.consent_hash} == {{env.AGENT_CONSENT_HASH}}'
on_failure: deny
- id: classify_scopes
type: builtin.policy.scope-classify
description: Map each requested scope name to its Tyk access_rights, rate, per, and
quota fields per the provider's policy file. The output is a complete SessionState
envelope ready to POST to /tyk/keys.
with:
requested: '${input.requested_scopes}'
- id: mint_key
call: tyk-gateway.addkey
description: One call. Returns key value, key_hash, expires, all scope/rate/quota
already attached. Compare with Kong (4 ops), Apigee (3 ops), AWS (3 ops).
with:
body:
alias: 'agent-${steps.verify_signature.agent_id}'
access_rights: '${steps.classify_scopes.tyk_access_rights}'
rate: '${steps.classify_scopes.rate}'
per: '${steps.classify_scopes.per}'
quota_max: '${steps.classify_scopes.quota_max}'
quota_renewal_rate: '${steps.classify_scopes.quota_renewal_rate}'
expires: '${steps.classify_scopes.expires_epoch}'
tags:
- 'agent'
- 'operator:${input.contact.operator}'
- 'skill:${input.skill_id}'
- 'naftiko-managed'
meta_data:
agent_id: '${steps.verify_signature.agent_id}'
operator: '${input.contact.operator}'
support_url: '${input.contact.support_url}'
consent_hash: '${input.consent_hash}'
signature_keyid: '${steps.verify_signature.keyid}'
- id: emit_audit
call: tyk-audit.emitevent
description: Emit to the customer's configured analytics destination. Tyk Dashboard
analytics or Tyk Pump destination if either is wired; otherwise a generic webhook.
with:
body:
event_type: agent.onboarded
agent_id: '${steps.verify_signature.agent_id}'
operator: '${input.contact.operator}'
support_url: '${input.contact.support_url}'
purpose: '${input.contact.purpose}'
skill_id: '${input.skill_id}'
scope_tier: '${steps.classify_scopes.target}'
access_rights_summary: '${steps.classify_scopes.access_rights_summary}'
consent_hash: '${input.consent_hash}'
signature_keyid: '${steps.verify_signature.keyid}'
key_id: '${steps.mint_key.key_hash}'
expires_epoch: '${steps.classify_scopes.expires_epoch}'
output:
agent_id: '${steps.verify_signature.agent_id}'
key_id: '${steps.mint_key.key_hash}'
credential:
type: Bearer
header: Authorization
value: '${steps.mint_key.key}'
expires_epoch: '${steps.classify_scopes.expires_epoch}'
revocation_url: '/v1/agents/${steps.verify_signature.agent_id}/revoke'
scope_tier: '${steps.classify_scopes.target}'
access_rights: '${steps.classify_scopes.tyk_access_rights}'
- name: revoke-agent
description: Atomic — delete the key. No cascading cleanup, no separate entity teardown.
inputs:
- { name: key_id, type: string, required: true }
steps:
- id: delete_key
call: tyk-gateway.deletekey
with:
keyID: '${input.key_id}'
- id: emit_revoke_audit
call: tyk-audit.emitevent
with:
body:
event_type: agent.revoked
key_id: '${input.key_id}'
output:
revoked: true
exposes:
- type: rest
namespace: tyk-agent-onboarding-rest
port: 8080
resources:
- path: /v1/agents/onboard
operations:
- { method: POST, name: onboardagent, call: orchestration.onboard-agent }
- path: /v1/agents/{agent_id}/revoke
operations:
- { method: POST, name: revokeagent, call: orchestration.revoke-agent }
- type: mcp
namespace: tyk-agent-onboarding-mcp
port: 9090
transport: http
tools:
- name: agent-register
hints: { readOnly: false, destructive: false, idempotent: false }
call: orchestration.onboard-agent
- name: agent-revoke
hints: { readOnly: false, destructive: true, idempotent: true }
call: orchestration.revoke-agent
- type: agent-skill
namespace: tyk-agent-onboarding-skill
description: 'Agent skill at /skills/onboard-agent.md. Tyk-specific addenda — (1)
one round trip from request to credential because Tyk issues the key with scope and
throttle already attached; (2) for GraphQL gateways, the requested scopes can declare
field-level access via the access_rights restricted_types vocabulary, giving agents
operating against GraphQL surfaces tighter scope than path-based gateways allow.'
skill:
name: onboard-agent
file: skills/onboard-agent.md