Template vs Instance Passports

TL;DR

  • Template Passport = the canonical identity of an agent created by the builder (who/what it is, baseline capabilities, Agent Passport Page, badge).
  • Instance Passport = a tenant/platform-specific install of that agent (who controls it here, local limits/regions/status, webhooks, suspend).
  • A Template can have many Instances. Instances inherit and can override only operational fields (limits, regions, status, contact/webhook), never the agent's core identity.

Why two kinds?

  • Platforms think in tenants (workspaces/stores/projects). They need per-tenant limits, assurance, audit, and suspend.
  • Builders need one canonical identity and badge for their agent, independent of where it runs.
  • This mirrors real-world IDs: model of a car (template) vs VIN of a car unit (instance).

Quick comparison

Aspect Template Passport (Builder) Instance Passport (Tenant/Platform)
Purpose Canonical identity & baseline policy Enforced, tenant-scoped runtime policy
Owner/Controller creator_* (builder org or user) controller_* (tenant org/user), platform_id, tenant_ref
Inheritance Source of truth for name/role/capabilities/description Inherits from template; can override limits/regions/status/contact/webhook
Status scope Global (revocation cascades) Local (can suspend one tenant only)
Webhooks Optional (builder notifications) Recommended (status/assurance/limits updates)
Verify usage For read-only identity or dev docs For gating routes (refunds, data export, PR merge)
Badge "Trusted Agent" (public Agent Passport page) "Verified Instance" (tenant-visible)
Billing Owned inside an APort org subscription Covered by the same org subscription or enterprise rollout
Suspend behavior Cascades to ALL instances Local only (one tenant)

When to use which ID?

Use the Template ID when:

  • Linking an About page or public badge.
  • Documenting the agent's baseline capabilities/description.
  • A platform is evaluating an agent before install (no tenant yet).
  • You want to fetch canonical metadata only.

Use the Instance ID when:

  • Enforcing risky routes (e.g., finance.payment.refund.v1, data.export.create.v1, repo.prcreate.v1).
  • You need tenant-specific limits/regions/assurance and per-tenantsuspend.
  • Emitting webhooks for one tenant's install.
  • Auditing who did what in this tenant.

Schema Differences

Template Passport Fields

{
  agent_id: "ap_template1234567890abcdef1234567890ab", // Template passport ID
  kind: "template",                      // Explicit template type
  name: "HappyRefunds Bot",             // Canonical name
  role: "CX refunds assistant",         // Canonical role
  description: "AI agent for...",       // Canonical description
  capabilities: [{"id": "finance.payment.refund", "params": {}}], // Core capabilities
  limits: {                             // Baseline limits
    refund_amount_max_per_tx: 100,
    refund_amount_daily_cap: 500
  },
  regions: ["US", "CA"],                // Baseline regions
  status: "active",                     // Global status
  assurance_level: "L2",                // Builder's assurance level
  creator_id: "ap_org_builderco",       // Who created the template
  creator_type: "org",
  // ... other standard passport fields
}

Instance Passport Fields

{
  agent_id: "ap_instance1234567890abcdef1234567890ab", // Instance passport ID
  kind: "instance",                     // Explicit instance type
  parent_agent_id: "ap_template1234567890abcdef1234567890ab", // Link to template
  platform_id: "gorgias",              // Which platform
  controller_id: "ap_org_acme",         // Tenant controller
  controller_type: "org",               // Controller type
  tenant_ref: "store_987",              // Platform's tenant ID
  
  // Inherited from template (can be overridden)
  name: "HappyRefunds Bot",             // Copied from template
  role: "CX refunds assistant",         // Copied from template
  description: "AI agent for...",       // Copied from template
  capabilities: [{"id": "finance.payment.refund", "params": {}}], // Inherited
  
  // Tenant-specific overrides
  limits: {                             // Tenant-specific limits
    refund_amount_max_per_tx: 50,          // Override: lower limit
    refund_amount_daily_cap: 200           // Override: lower cap
  },
  regions: ["US"],                      // Override: US only
  status: "active",                     // Tenant-specific status
  contact: "[email protected]",         // Tenant contact
  webhook_url: "https://acme.com/webhook", // Tenant webhook
  
  // Owner is the tenant, not the builder
  owner_id: "ap_org_acme",              // Tenant owns this instance
  owner_type: "org",
  owner_display: "Acme Corp",
  
  // Timestamps
  created_at: "2024-01-16T10:00:00Z",
  updated_at: "2024-01-16T10:00:00Z",
  updated_from_parent_at: "2024-01-16T10:00:00Z", // When template last updated
}

Billing Implications

Template Passports

  • Subscription owner: The APort organization that owns the template.
  • Commercial model: Covered by Free, Team, Enterprise, or manual pilot access depending on the organization workspace.
  • Value: Public identity, badges, documentation, baseline policy, and shared setup for instance issuance.

Instance Passports

  • Subscription owner: Usually the same organization workspace or the enterprise rollout account managing the deployment.
  • Commercial model: Covered by the org subscription or enterprise agreement; do not create a separate per-instance billing source of truth.
  • Scale-aware: More tenants means more operational evidence, controls, and support scope, but entitlement checks should still be org-scoped.
  • Value: Policy enforcement, per-tenant controls, webhooks, and Verifiable Attestation.

Billing Example

Template: "HappyRefunds Bot" (ap_template1234567890abcdef1234567890ab)
ā”œā”€ā”€ Owner workspace: Acme AI Apps (Team or Enterprise)
ā”œā”€ā”€ Instance 1: Gorgias tenant "store_987" (ap_instance1234567890abcdef10000001)
│   └── Covered by Acme AI Apps subscription or rollout agreement
ā”œā”€ā”€ Instance 2: Zendesk tenant "workspace_456" (ap_instance1234567890abcdef10000002)
│   └── Covered by Acme AI Apps subscription or rollout agreement
└── Instance 3: Shopify tenant "shop_789" (ap_instance1234567890abcdef10000003)
    └── Covered by Acme AI Apps subscription or rollout agreement

Total: one org-scoped commercial relationship, not separate billing truth per passport.

Suspension Behavior

Template Suspension (Cascading)

When a template is suspended or revoked:

  1. All instances are automatically suspended
  2. Webhooks are sent to each tenant with suspension reason
  3. Verifiable Attestation is created for each instance suspension
  4. Policy enforcement immediately blocks all instance usage
  5. Builder can resume template to unsuspend all instances
// Template suspension cascades to all instances
await suspendTemplateInstances(kv, templateId, webhookConfig, registryPrivateKey, actor);
// Result: { suspended: 15, errors: [] } // 15 instances suspended

Instance Suspension (Local)

When an instance is suspended:

  1. Only that tenant is affected
  2. Other instances continue working normally
  3. Template remains active
  4. Platform can suspend/resume per tenant
// Instance suspension is local only
await updatePassportStatus(kv, instanceId, "suspended", reason, actor);
// Result: Only ap_instance1234567890abcdef10000001 is suspended
// Other instances remain active

Creation Flow

1. Template Creation (Builder)

curl -X POST "https://api.aport.io/api/admin/create" \
  -H "Authorization: Bearer YOUR_ADMIN_API_KEY" \
  -d '{
    "name": "HappyRefunds Bot",
    "role": "Support Refunds",
    "description": "Refund helper for customer support",
    "capabilities": [{"id": "finance.payment.refund", "params": {}}],
    "limits": {
      "refund_amount_max_per_tx": 100,
      "refund_amount_daily_cap": 500
    },
    "regions": ["US", "CA"],
    "contact": "[email protected]",
    "kind": "template",
    "status": "active"
  }'
# Response: { "agent_id": "ap_template1234567890abcdef1234567890ab", "kind": "template" }

2. Instance Creation (Platform)

curl -X POST "https://api.aport.io/api/passports/ap_template1234567890abcdef1234567890ab/instances" \
  -H "Authorization: Bearer YOUR_PLATFORM_API_KEY" \
  -d '{
    "platform_id": "gorgias",
    "controller_id": "ap_org_acme",
    "controller_type": "org",
    "tenant_ref": "store_987",
    "overrides": {
      "limits": {
        "refund_amount_max_per_tx": 50,
        "refund_amount_daily_cap": 200
      },
      "regions": ["US"],
      "status": "active",
      "contact": "[email protected]"
    }
  }'
# Response: { "agent_id": "ap_instance1234567890abcdef1234567890ab", "kind": "instance" }

Policy Enforcement

Template Verification

curl "https://api.aport.io/api/verify/ap_template1234567890abcdef1234567890ab"
# Returns: Canonical identity and baseline policy
# Use case: Documentation, badges, public Agent Passport pages

Instance Verification (Runtime Enforcement)

curl -X POST "https://api.aport.io/api/verify/policy/finance.payment.refund.v1" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "ap_instance1234567890abcdef1234567890ab",
    "context": {
      "amount": 25,
      "currency": "USD",
      "tenant_id": "store_987"
    }
  }'
# Returns: { "allow": true, "passport": {...}, "evaluation": {...} }
# Use case: Gating refunds, data exports, PR merges

Inheritance Rules

Fields Inherited from Template (Read-Only)

  • name - Agent's canonical name
  • role - Agent's canonical role
  • description - Agent's canonical description
  • capabilities - Core capability set
  • assurance_level - Builder's assurance level
  • attestations - Builder's attestations
  • links - Builder's links (homepage, repo, docs)
  • categories - Agent categories
  • framework - Agent framework
  • model_info - Model information

Fields Overrideable in Instance

  • limits - Tenant-specific limits
  • regions - Tenant-specific regions
  • status - Tenant-specific status
  • contact - Tenant contact information
  • webhook_url - Tenant webhook endpoint
  • owner_id - Tenant owner
  • owner_type - Tenant owner type
  • owner_display - Tenant display name

Fields Unique to Instance

  • parent_agent_id - Link to template
  • platform_id - Platform identifier
  • controller_id - Tenant controller
  • controller_type - Controller type
  • tenant_ref - Platform's tenant ID
  • updated_from_parent_at - Last template sync

Webhook Events

Template Events

  • passport.created - Template created
  • passport.updated - Template updated
  • status.changed - Template status changed (cascades to instances)

Instance Events

  • passport.created - Instance created
  • passport.updated - Instance updated
  • status.changed - Instance status changed (local only)
  • assurance.updated - Instance assurance updated
  • limits.exceeded - Instance limits exceeded

Best Practices

For Builders

  1. Create templates with comprehensive baseline policies
  2. Use template IDs for public badges and documentation
  3. Monitor template status - suspension affects all instances
  4. Keep templates updated - changes propagate to instances

For Platforms

  1. Create instances for each tenant installation
  2. Use instance IDs for policy enforcement
  3. Set tenant-specific limits based on customer tier
  4. Register webhooks for real-time status updates
  5. Implement per-tenant suspend for incident response

For Policy Enforcement

  1. Always use instance IDs in middleware
  2. Check both template and instance status
  3. Respect tenant-specific limits and regions
  4. Log Verifiable Attestation with instance context

Migration from Legacy Passports

Legacy passports (without kind field) are treated as templates by default:

// Legacy passport
{
  "agent_id": "ap_abc123",  // No kind field
  "name": "Legacy Agent",
  // ... other fields
}

// Treated as:
{
  "agent_id": "ap_abc123",
  "kind": "template",  // Default behavior
  "name": "Legacy Agent",
  // ... other fields
}

To create instances from legacy templates, use the same instance creation API with the legacy agent ID as the template ID.


Troubleshooting

Common Issues

Q: Why can't I suspend just one tenant?
A: You're using the template ID instead of the instance ID. Use the instance ID for tenant-specific suspension.

Q: Why did all my instances get suspended?
A: The template was suspended, which cascades to all instances. Check template status and resume if needed.

Q: Why are my limits not being enforced?
A: You're using the template ID for policy enforcement. Use the instance ID to get tenant-specific limits.

Q: Why can't I override capabilities in an instance?
A: Capabilities are inherited from the template and cannot be overridden. Update the template to change capabilities.

Q: Why is my webhook not firing?
A: Webhooks are instance-specific. Make sure you're using the instance ID and have registered webhooks for the instance.


API Reference

Template Operations

  • POST /api/admin/create - Create template
  • GET /api/verify/{template_id} - Get template info
  • PATCH /api/passports/{template_id} - Update template
  • POST /api/passports/{template_id}/status - Suspend/revoke template

Instance Operations

  • POST /api/passports/{template_id}/instances - Create instance
  • GET /api/verify/{instance_id} - Get instance info
  • PATCH /api/passports/{instance_id} - Update instance
  • POST /api/passports/{instance_id}/status - Suspend instance
  • GET /api/passports/{template_id}/instances - List instances

Policy Verification

  • POST /api/verify/policy/{pack_id} - Verify policy compliance
  • GET /api/verify/{agent_id} - Get passport data for verification