Production Guide
Best practices for deploying Skippy in production.
Environments
| Environment | Base URL | Use For |
|---|---|---|
| Test | https://testapi.skippy.id | Development, testing |
| Production | https://api.skippy.id | Live applications |
warning
Test credentials are not verifiable in production. Use production API for real credentials.
API Key Security
Do
// ✓ Use environment variables
const apiKey = process.env.SKIPPY_API_KEY;
// ✓ Rotate keys periodically
// ✓ Use separate keys per environment
// ✓ Limit key scope to required operations
Don't
// ✗ Never hardcode keys
const apiKey = "sk_live_abc123"; // WRONG
// ✗ Never commit to version control
// ✗ Never expose in client-side code
// ✗ Never share keys between environments
Key Rotation
- Create new API key in dashboard
- Update your application with new key
- Verify new key works
- Delete old key
Error Handling
Always handle API errors gracefully:
async function issueCredential(data) {
try {
const response = await fetch('https://api.skippy.id/v1/openidvc/offer', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.SKIPPY_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
if (!response.ok) {
const error = await response.json();
if (response.status === 429) {
// Rate limited - implement backoff
await sleep(error.retryAfter * 1000);
return issueCredential(data);
}
throw new Error(error.errors[0].detail);
}
return response.json();
} catch (err) {
console.error('Credential issuance failed:', err);
// Alert your monitoring system
throw err;
}
}
Rate Limiting
| Endpoint | Limit |
|---|---|
| Credential issuance | 100/min |
| Verification requests | 200/min |
| Template operations | 50/min |
Handle rate limits:
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function withRetry(fn, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (err) {
if (err.status === 429 && i < maxRetries - 1) {
await sleep(Math.pow(2, i) * 1000); // Exponential backoff
continue;
}
throw err;
}
}
}
Webhook Security
Always verify webhook signatures. See Webhook Verification for implementation.
// Verify before processing
if (!verifyWebhookSignature(req)) {
return res.status(401).send('Invalid signature');
}
Monitoring
Key Metrics to Track
| Metric | Alert Threshold |
|---|---|
| API error rate | > 5% |
| Issuance success rate | < 95% |
| Verification latency | > 3s |
| Webhook delivery failures | > 3 consecutive |
Logging Best Practices
// Log credential operations (without sensitive data)
console.log({
event: 'credential.issued',
offerId: offer.id,
templateId: template.id,
timestamp: new Date().toISOString()
// Don't log: recipientEmail, credentialData
});
Compliance Checklist
Before going live:
- API keys stored securely (not in code)
- HTTPS enforced for all webhook endpoints
- Webhook signatures verified
- Error handling implemented with retries
- Rate limiting handled gracefully
- Logging in place (no PII in logs)
- Monitoring configured with alerts
- Data retention policy defined
- Backup strategy for credential records
Data Retention
| Data Type | Recommended Retention |
|---|---|
| Credential offers | 90 days after completion |
| Verification results | 30 days |
| Audit logs | 1 year minimum |
| Revocation records | Indefinite |
Support Escalation
| Issue | Contact |
|---|---|
| API errors | Contact support@skippy.id |
| Integration help | support@skippy.id |
| Security concerns | security@skippy.id |
| Status updates | status.skippy.id |
Next Steps
- Webhooks — Set up real-time notifications
- API Reference — Full endpoint documentation