TL;DR
- Ephemeral credentials (15 minutes) minimize attack surface. Use for high-risk operations (refunds, PII access).
- Medium-term (7-90 days) balances security and operations. Use for project-based work, seasonal operations.
- Long-term (6-12 months) forces periodic compliance checks. Use for annual contracts, regulatory compliance.
- Perpetual (never expires) simplifies operations. Use for core infrastructure, production services.
- Default is perpetual (backward compatible). Opt-in to expiry for security-sensitive agents.
- W3C Verifiable Credential compatible—export passports with expiry as VCs for ecosystem interoperability.
The moment you realize credentials need expiration
A retailer's support agent issued three $500 refunds in an hour. Finance couldn't tell which agent did it or who owned it. Nobody knew the limit. Everyone guessed. With passport expiry, that agent would have expired after 15 minutes. Problem solved before it started. In this guide, I'll walk through APort's flexible passport expiry system—from 15-minute ephemeral credentials to perpetual passports. When to use each, how to implement it, and why it matters for production security.
Why Expiry Matters: The Attack Surface Problem
Research shows:
- 96 non-human identities (NHIs) per employee in financial services
- 60% of attacks involve compromised identities
- Short-lived credentials minimize exposure window
- 15-minute ephemeral: Attack window = 15 minutes max
- 7-day rotating: Attack window = 7 days max
- Perpetual: Attack window = forever (until manual revocation)
Use Cases: When to Use Each Expiry Duration
1. Ephemeral Credentials (15 minutes - 24 hours)
When to use:
- Temporary contractor access: Grant limited-time access to external contractors
- Demo environments: Create time-limited agents for product demonstrations
- Research projects: Issue short-lived credentials for academic research
- Incident response: Temporary elevated privileges during security incidents
- One-time tasks: Agents that should only operate for a specific time window
POST /api/passports/create
Authorization: Bearer <ADMIN_TOKEN>
{
"name": "Incident Response Bot",
"expires_in_days": 0.0104, // 15 minutes
"capabilities": ["security.incident.read", "security.logs.export"],
"limits": {
"max_actions_per_min": 10
}
}
Note: 0.0104 days = 15 minutes (minimum allowed duration) Benefits:
- ✅ Minimizes attack surface (aligned with Zero Trust principles)
- ✅ Reduces risk of credential theft
- ✅ Enforces time-boxed access control
- ✅ Automatic credential revocation (no manual cleanup)
2. Medium-Term Credentials (1 week - 90 days)
When to use:
- Project-based work: Agents tied to specific project timelines
- Seasonal operations: Retail bots for holiday shopping periods
- Contract workers: Align agent access with contract duration
- Beta testing: Time-limited access for testing new features
- Audit compliance: Force periodic credential rotation
POST /api/passports/create
{
"name": "Q4 Sales Assistant",
"expires_in_days": 90,
"capabilities": ["sales.data.read", "analytics.view"],
"limits": {
"max_export_rows": 1000
}
}
Benefits:
- ✅ Balances security with operational flexibility
- ✅ Reduces manual credential management
- ✅ Enforces periodic re-verification
- ✅ Aligns with compliance requirements (e.g., quarterly reviews)
3. Long-Term Credentials (6 months - 1 year)
When to use:
- Annual contracts: Agents with yearly renewal cycles
- Regulatory compliance: Financial agents requiring annual certification
- Enterprise deployments: Large-scale agents with yearly audits
- License-based access: Agents tied to annual software licenses
POST /api/passports/create
{
"name": "Financial Transaction Agent",
"expires_in_days": 365,
"capabilities": ["finance.payment.refund", "finance.transaction.execute"],
"limits": {
"max_refund_usd_per_tx": 10000,
"max_refund_usd_daily_cap": 100000
}
}
Benefits:
- ✅ Forces periodic compliance checks
- ✅ Aligns with annual security audits
- ✅ Reduces stale credential accumulation
- ✅ Supports regulatory requirements (e.g., PCI-DSS annual reviews)
4. Perpetual Credentials (Never Expires)
When to use:
- Core infrastructure agents: Critical system agents that must always be available
- Production services: Customer-facing agents with 24/7 availability requirements
- Internal tooling: Developer tools and CI/CD agents
- Legacy systems: Agents where expiration would cause operational issues
POST /api/passports/create
{
"name": "Production Customer Support Bot",
"never_expires": true, // or omit expiry fields (defaults to perpetual)
"capabilities": ["support.ticket.read", "support.ticket.update"],
"limits": {
"max_actions_per_min": 100
}
}
Benefits:
- ✅ No operational disruption from credential expiration
- ✅ Simplifies credential management for stable systems
- ✅ Suitable for high-availability requirements
- ⚠️ Requires manual lifecycle management and periodic audits
API Reference: How to Create Passports with Expiry
Option 1: Specify Exact Expiration Timestamp
POST /api/passports/create
Authorization: Bearer <ADMIN_TOKEN>
Content-Type: application/json
{
"name": "Demo Agent",
"expires_at": "2025-12-31T23:59:59Z",
"capabilities": ["data.read"],
"limits": {}
}
Use when: You know the exact expiration date (e.g., contract end date, project deadline).
Option 2: Use Convenience Field (Days from Now)
POST /api/passports/create
{
"name": "Short-Term Agent",
"expires_in_days": 7,
"capabilities": ["data.read"],
"limits": {}
}
Use when: You want expiration relative to creation time (e.g., "expires in 7 days"). Supported durations:
- Minimum: 0.0104 days (15 minutes) for non-admin users
- Maximum: 3650 days (10 years) for all users
- Fractional days: Supported (e.g., 0.5 = 12 hours, 0.25 = 6 hours)
Option 3: Perpetual Credentials (Default)
POST /api/passports/create
{
"name": "Permanent Agent",
"never_expires": true, // Explicit
"capabilities": ["data.read"],
"limits": {}
}
// Or simply omit expiry fields (defaults to perpetual)
POST /api/passports/create
{
"name": "Permanent Agent",
"capabilities": ["data.read"],
"limits": {}
}
Use when: Agent should never expire (core infrastructure, production services).
Verification Response: How to Check Expiry
When verifying a passport with expiry, the response includes detailed expiry information:
GET /api/verify/ap_abc123
Response:
{
"success": true,
"data": {
"agent_id": "ap_abc123",
"status": "active",
"expires_at": "2025-12-31T23:59:59Z"
},
"expiry": {
"expires_in_seconds": 86400,
"expires_in_minutes": 1440,
"expires_in_hours": 24,
"expires_in_days": 1,
"severity": "warning",
"message": "Passport expires in 24 hours"
}
}
Expiry Severity Levels
| Severity | Threshold | Description | Action |
|---|---|---|---|
ok
|
> 24 hours | Passport is valid with plenty of time remaining | None |
warning
|
1-24 hours | Approaching expiration - consider renewal | Monitor, prepare renewal |
critical
|
< 1 hour | Imminent expiration - urgent action required | Renew immediately |
expired
|
Expired | Passport is no longer valid - access denied | Renew or create new passport |
Use cases:
ok: Normal operation, no action neededwarning: Set up alerts, prepare renewal workflowcritical: Trigger automatic renewal or notify adminsexpired: Block access, require renewal
Error Handling: What Happens When a Passport Expires
When a passport is expired, verification returns 403 Forbidden:
{
"error": "passport_expired",
"message": "Passport expired at 2025-01-15T10:30:00Z",
"requestId": "verify_123456"
}
Response headers:
HTTP/1.1 403 Forbidden
X-Expiry-Status: expired
X-Expired-At: 2025-01-15T10:30:00Z
Retry-After: 0
What this means:
- Agent can't perform authorized actions: All verification calls return 403
- No automatic renewal: Must explicitly update passport with new expiry
- Audit trail preserved: Expired passports remain in system for compliance
Validation Rules: What's Allowed
Minimum Duration
- Non-admin users: 15 minutes (0.0104 days)
- Admin users: No minimum (can create credentials with any duration)
Maximum Duration
- All users: 10 years (3650 days)
Mutual Exclusivity
- Cannot specify both
expires_atandexpires_in_days - Cannot specify expiry with
never_expires=true
{
"error": "validation_error",
"message": "Cannot specify both expires_at and expires_in_days"
}
W3C Verifiable Credential Support
Passports with expiry can be exported as W3C Verifiable Credentials:
GET /api/passports/ap_abc123?format=vc
Response:
{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://raw.githubusercontent.com/aporthq/aport-spec/refs/heads/main/oap/vc/context-oap-v1.jsonld"
],
"type": ["VerifiableCredential", "OAPPassportCredential"],
"credentialSubject": {
"agent_id": "ap_abc123",
"expires_at": "2025-12-31T23:59:59Z"
},
"issuer": "https://aport.io",
"issuanceDate": "2025-01-16T00:00:00Z",
"expirationDate": "2025-12-31T23:59:59Z",
"proof": {
"type": "Ed25519Signature2020",
"created": "2025-01-16T00:00:00Z",
"verificationMethod": "https://aport.io/keys/reg-2025-01",
"proofPurpose": "assertionMethod",
"proofValue": "z3s2C9y8B1vF4..."
}
}
Expiry mapping:
expires_at→ VCexpirationDatenever_expires=true→ VCexpirationDateset to far future (2099-12-31)- No expiry → VC
expirationDatedefaults to 1 year from issuance
- ✅ Ecosystem interoperability: Works with other VC/DID systems
- ✅ Future-proof: W3C standards won't be deprecated
- ✅ Vendor-neutral: Not locked into APort format
Best Practices: Choosing the Right Expiry Duration
Risk-Based Selection
| Risk Level | Use Case | Recommended Duration | Example |
|---|---|---|---|
| Critical | Financial transactions, PII access | 15-60 minutes | Payment processing, data exports |
| High | Admin operations, data exports | 1-24 hours | Incident response, security operations |
| Medium | Customer support, content moderation | 7-30 days | Support bots, moderation agents |
| Low | Read-only analytics, monitoring | 90-365 days | Analytics agents, monitoring bots |
| Stable | Core infrastructure, production services | Never expires | Production support bots, CI/CD agents |
Implementation: Renewal Workflows
For short-lived credentials:
// Check expiry before critical operations
const verification = await fetch(`/api/verify/${agentId}`);
const data = await verification.json();
if (data.expiry?.severity === 'critical') {
// Trigger renewal process
await renewPassport(agentId);
}
// Or set up automatic renewal
if (data.expiry?.expires_in_hours < 1) {
await updatePassport(agentId, {
expires_in_days: 0.0104 // Renew for another 15 minutes
});
}
For medium-term credentials:
# Python example
from aport import APortClient
client = APortClient(api_key=os.environ['APORT_API_KEY'])
# Check expiry weekly
verification = client.verify(passport.agent_id)
if verification['expiry']['expires_in_days'] < 7:
# Renew for another 90 days
client.passports.update(
passport.agent_id,
expires_in_days=90
)
Monitoring: Set Up Alerts
Recommended alerts:
- 24 hours before expiry: Warning alert (prepare renewal)
- 1 hour before expiry: Critical alert (renew immediately)
- On expiry: Incident notification (access denied)
// Check expiry status and send alerts
const checkExpiry = async (agentId: string) => {
const verification = await fetch(`/api/verify/${agentId}`);
const data = await verification.json();
if (data.expiry?.severity === 'warning') {
// Send Slack alert
await sendSlackAlert({
channel: '#security',
message: `Agent ${agentId} expires in ${data.expiry.expires_in_hours} hours`,
});
}
if (data.expiry?.severity === 'critical') {
// Send PagerDuty alert
await sendPagerDutyAlert({
severity: 'critical',
message: `Agent ${agentId} expires in ${data.expiry.expires_in_minutes} minutes`,
});
}
};
Documentation: Expiry Rationale
Always document why a specific expiry duration was chosen:
{
"name": "PCI Compliance Agent",
"expires_in_days": 90,
"description": "Payment processing agent with quarterly compliance review",
"metadata": {
"expiry_rationale": "PCI-DSS requires quarterly access reviews",
"compliance_requirement": "PCI-DSS 3.2.1",
"review_frequency": "quarterly"
}
}
Why this matters:
- Audit trail: Auditors can verify expiry rationale
- Compliance: Demonstrates alignment with regulatory requirements
- Team knowledge: Future developers understand the decision
Security Considerations: The Zero Trust Angle
Ephemeral Credentials Reduce Attack Surface
The math:
- 15-minute ephemeral: Attack window = 15 minutes max
- 7-day rotating: Attack window = 7 days max
- Annual + audit: Attack window = 365 days max
- Perpetual + monitoring: Attack window = forever
Balance Security vs. Operations
| Approach | Security | Operational Complexity | Use When |
|---|---|---|---|
| 15-minute ephemeral | ✅✅✅ Highest | ⚠️ High (frequent renewals) | High-risk operations |
| 7-day rotating | ✅✅ High | ✅ Medium | Project-based work |
| Annual + audit | ✅ Medium | ✅✅ Low | Stable systems |
| Perpetual + monitoring | ⚠️ Lower | ✅✅✅ Lowest | Core infrastructure |
Real-world trade-off: A fintech company uses 15-minute ephemeral credentials for payment processing agents. The operational complexity (automatic renewal every 15 minutes) is worth the security benefit (15-minute attack window). Result: Zero credential-based attacks in 12 months.
Compliance Alignment
| Regulation | Requirement | APort Solution | Example |
|---|---|---|---|
| PCI-DSS | Quarterly access reviews |
expires_in_days: 90
|
Payment processing agents |
| SOC 2 | Regular credential rotation |
expires_in_days: 180
|
Data access agents |
| GDPR | Time-limited data access |
expires_in_days: 30
|
Data export agents |
| HIPAA | Annual security reviews |
expires_in_days: 365
|
Healthcare data agents |
Real-world example: A healthcare company uses 365-day credentials for their HIPAA-compliant data access agents. Credentials expire annually, forcing security reviews and access audits. Result: Passed HIPAA audit with zero findings (auditors loved the automatic expiry).
Migration Guide: Adding Expiry to Existing Passports
Existing Perpetual Passports
All existing passports remain perpetual by default:
never_expiresimplicitly set totrue- No
expires_atfield present - No action required
Adding Expiry to Existing Passports
Update existing passports via the API:
PUT /api/passports/ap_abc123
Authorization: Bearer <ADMIN_TOKEN>
{
"expires_in_days": 30
}
This sets expiration 30 days from the update time. Use cases:
- Security audit: Add expiry to high-risk agents
- Compliance requirement: Align with regulatory mandates
- Operational change: Transition from perpetual to time-limited
FAQs: Common Questions About Expiry
Q: What happens when a passport expires?
A: Verification endpoints return 403 Forbidden with error passport_expired. The agent can no longer perform authorized actions. Passport remains in system for audit trail.
Q: Can expired passports be renewed?
A: Yes. Update the passport with a new expires_at or expires_in_days to extend validity:
PUT /api/passports/ap_abc123
{
"expires_in_days": 30 // Renew for another 30 days
}
Q: How do I monitor expiring passports?
A: Check the expiry.severity field in verification responses. Values of warning or critical indicate approaching expiration:
const verification = await fetch(`/api/verify/${agentId}`);
const data = await verification.json();
if (data.expiry?.severity === 'warning') {
// Set up renewal workflow
}
Q: Can I use fractional days for expires_in_days?
A: Yes! expires_in_days: 0.0104 = 15 minutes. Minimum is 0.0104 days (15 minutes) for non-admin users.
Common fractional values:
0.0104= 15 minutes0.0208= 30 minutes0.0417= 1 hour0.5= 12 hours1= 24 hours
Q: What's the default if I don't specify expiry?
A: Passports are perpetual by default (never_expires: true). This ensures backward compatibility with existing passports.
Q: Can admins override the 15-minute minimum?
A: Yes. Admin users can create passports with any expiry duration, including < 15 minutes. Non-admin users are limited to 15-minute minimum.
Code Examples: Real Implementation
TypeScript SDK
import { APortClient } from '@aporthq/sdk-node';
const client = new APortClient({ apiKey: process.env.APORT_API_KEY });
// Create ephemeral passport (15 minutes)
const ephemeral = await client.passports.create({
name: 'Incident Response Bot',
expires_in_days: 0.0104, // 15 minutes
capabilities: ['security.incident.read', 'security.logs.export'],
limits: { max_actions_per_min: 10 }
});
// Check expiry before operation
const verification = await client.verify(ephemeral.agent_id);
if (verification.expiry?.severity === 'expired') {
throw new Error('Agent credentials expired');
}
// Renew if critical
if (verification.expiry?.severity === 'critical') {
await client.passports.update(ephemeral.agent_id, {
expires_in_days: 0.0104 // Renew for another 15 minutes
});
}
Python SDK
from aport import APortClient
client = APortClient(api_key=os.environ['APORT_API_KEY'])
# Create 7-day passport
passport = client.passports.create(
name='Project Beta Tester',
expires_in_days=7,
capabilities=['data.read', 'analytics.view'],
limits={'max_export_rows': 1000}
)
# Monitor expiry
verification = client.verify(passport.agent_id)
if verification['expiry']['severity'] in ['warning', 'critical']:
# Trigger renewal workflow
renew_passport(passport.agent_id)
cURL Examples
# Create 15-minute ephemeral passport
curl -X POST https://api.aport.io/api/admin/create \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Incident Response Bot",
"expires_in_days": 0.0104,
"capabilities": ["security.incident.read"]
}'
# Check expiry status
curl https://api.aport.io/api/verify/ap_abc123
# Renew expired passport
curl -X PATCH https://api.aport.io/api/admin/status \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "ap_abc123",
"expires_in_days": 30
}'
Summary: What You Get
APort's passport expiry system provides:
- ✅ Flexibility: Supports ephemeral (15 min), medium-term (days-weeks), long-term (months-year), and perpetual credentials
- ✅ Security: Reduces attack surface through time-limited access
- ✅ Compliance: Aligns with PCI-DSS, SOC 2, GDPR, HIPAA requirements
- ✅ Standards: W3C Verifiable Credential compatible
- ✅ Backward Compatible: Existing passports remain perpetual
- ✅ Developer-Friendly: Simple API with
expires_in_daysconvenience field
- 73% fraud reduction (fintech using 15-minute ephemeral credentials)
- 50% reduction in stale credentials (e-commerce using 90-day rotation)
- Zero mandate breaches (wealth management using 365-day credentials)
- 99.99% uptime (SaaS using perpetual credentials for production)
- 📚 Read the full API documentation
- 🔧 Try the interactive API explorer
- 💬 Join our developer community for support
- 🚀 Get started with your first passport
Last updated: November 2025 | Version: OAP v1.0