Skip to main content

Tutorial: Manage API credentials

Learn how to create, rotate, and manage API tokens and OAuth apps for the iGUIDE Portal API.

Time to complete: ~15 minutes

What you'll learn:

  • Create API tokens via the Portal UI and programmatically via API
  • List and inspect your credentials
  • Rotate API tokens with zero downtime
  • Create and manage OAuth applications
  • Update OAuth app settings
  • Rotate OAuth app secrets securely
  • Clean up and delete unused credentials

Prerequisites

Before you begin, you need:

  • A user account on the iGUIDE Portal
  • curl or any HTTP client
  • Basic understanding of API authentication headers
API token vs OAuth app

Not sure which credential type to use? See API tokens and OAuth apps to understand the differences and when to use each.

Step 1: Set up your environment

To follow along with the examples in this tutorial, store your credentials as environment variables:

export APP_ID="your-app-id-here"
export APP_TOKEN="your-access-token-here"

All API Management endpoints require these two authentication headers:

  • X-Plntr-App-Id: Your token ID or OAuth client ID
  • X-Plntr-App-Token: Your token value or OAuth access token

Step 2: Create an API token via the Portal UI

The easiest way to create your first API token is through the Portal UI.

  1. Log in to the iGUIDE Portal
  2. Navigate to SettingsAPI ManagementAPI Tokens tab
  3. Click Create API Token
  4. Fill in the required information:
    • Name: A descriptive name for your token (e.g., "Production API Integration")
    • Description (optional): Additional details about the token's purpose
    • Scopes: Select the permissions your token needs (e.g., iguide.list, iguide.ro, iguide.tasks)
  5. Click Create
  6. Copy both values immediately:
    • Token ID — this is your X-Plntr-App-Id
    • Token Value — this is your X-Plntr-App-Token
Save your token immediately

The token value is only shown once when created. Save it securely in a password manager or secure vault—you won't be able to retrieve it later. If you lose it, you'll need to rotate the token to get a new value.

Step 3: Create an API token via the API

For automation and CI/CD workflows, you can create API tokens programmatically.

curl -X POST https://manage.youriguide.com/api/v1/integrations/tokens \
-H "X-Plntr-App-Id: $APP_ID" \
-H "X-Plntr-App-Token: $APP_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Staging Deployment Token",
"description": "Token for staging environment deployments",
"scopes": ["iguide.list", "iguide.ro", "iguide.tasks"],
"expiresAt": "2028-02-21T12:00:00Z"
}'

Response:

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Staging Deployment Token",
"description": "Token for staging environment deployments",
"scopes": ["iguide.list", "iguide.ro", "iguide.tasks"],
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
"tokenHint": "...ssw5c",
"expiresAt": "2028-02-21T12:00:00Z",
"createdAt": "2026-02-21T12:00:00Z"
}

Save the returned token value—it's only shown once. Use the id as your X-Plntr-App-Id and the token as your X-Plntr-App-Token in future requests.

Step 4: List and inspect your tokens

Retrieve all API tokens for your account to see their details, usage status, and expiration dates.

curl https://manage.youriguide.com/api/v1/integrations/tokens \
-H "X-Plntr-App-Id: $APP_ID" \
-H "X-Plntr-App-Token: $APP_TOKEN"

Response:

[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Production API Token",
"description": "Token for production environment",
"scopes": ["iguide.list", "iguide.ro", "iguide.tasks"],
"tokenHint": "...abc123",
"expiresAt": "2028-02-21T12:00:00Z",
"createdAt": "2026-02-21T12:00:00Z",
"lastUsedAt": "2026-02-23T10:30:00Z"
},
{
"id": "660e8400-e29b-41d4-a716-446655440001",
"name": "Staging Deployment Token",
"description": "Token for staging environment deployments",
"scopes": ["iguide.list", "iguide.ro", "iguide.tasks"],
"tokenHint": "...ssw5c",
"expiresAt": "2028-02-21T12:00:00Z",
"createdAt": "2026-02-21T12:00:00Z",
"lastUsedAt": null
}
]
Token security

Notice that the full token values are never returned in list or get operations. Only the last 4 characters (tokenHint) are shown for identification purposes. This protects your credentials from being exposed in logs or API responses.

Step 5: Rotate a token with zero downtime

API token rotation allows you to generate a new token value while keeping the same token ID. The old token remains valid during a grace period (default: 7 days), enabling zero-downtime credential updates.

Rotate the token:

curl -X POST https://manage.youriguide.com/api/v1/integrations/tokens/550e8400-e29b-41d4-a716-446655440000/rotate \
-H "X-Plntr-App-Id: $APP_ID" \
-H "X-Plntr-App-Token: $APP_TOKEN"

Response:

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Production API Token",
"description": "Token for production environment",
"scopes": ["iguide.list", "iguide.ro", "iguide.tasks"],
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.NEW_TOKEN_VALUE.xyz789",
"tokenHint": "...xyz789",
"expiresAt": "2028-02-21T12:00:00Z",
"createdAt": "2026-02-21T12:00:00Z",
"previousToken": {
"hint": "...ssw5c",
"status": "active",
"expiresAt": "2026-02-28T12:00:00Z"
}
}

Zero-downtime rotation workflow:

  1. Call the rotate endpoint to get a new token value
  2. Grace period begins—both old and new tokens are valid
  3. Deploy the new token to your applications
  4. Test that your applications work with the new token
  5. Optionally revoke the previous token early:
curl -X POST https://manage.youriguide.com/api/v1/integrations/tokens/550e8400-e29b-41d4-a716-446655440000/revokePrevious \
-H "X-Plntr-App-Id: $APP_ID" \
-H "X-Plntr-App-Token: $APP_TOKEN"
  1. If you don't revoke early, the old token automatically expires after 7 days
Rotation best practice

Use the grace period to test your new token in staging before revoking the old one. This ensures zero-downtime credential updates and lets you quickly roll back if something goes wrong.

Step 6: Create an OAuth app

OAuth apps are ideal for user-facing applications that need OAuth 2.0 authorization flows. Create an OAuth app programmatically:

curl -X POST https://manage.youriguide.com/api/v1/integrations/apps \
-H "X-Plntr-App-Id: $APP_ID" \
-H "X-Plntr-App-Token: $APP_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"clientName": "My Web Application",
"description": "OAuth app for web application",
"redirectUris": ["https://myapp.com/oauth/callback"],
"logoUri": "https://myapp.com/logo.png",
"scope": ["iguide.list", "iguide.ro"]
}'

Response:

{
"clientId": "660e8400-e29b-41d4-a716-446655440001",
"clientName": "My Web Application",
"description": "OAuth app for web application",
"redirectUris": ["https://myapp.com/oauth/callback"],
"logoUri": "https://myapp.com/logo.png",
"scope": ["iguide.list", "iguide.ro"],
"clientSecret": "sk_live_51H8F2KLbZqZ1X2Y3Z4W5V6U7T8S9R0Q1P2O3N4M5L6K7J8I9H0G1F2E3D4C5B6A",
"clientSecretHint": "...5B6A",
"clientIdIssuedAt": "2026-02-21T12:00:00Z",
"updatedAt": "2026-02-21T12:00:00Z"
}
Save the client secret immediately

The clientSecret is only returned once when you create the app. Store it securely in a password manager or secure vault—treat it like a password. If you lose it, you'll need to rotate the secret to get a new value.

Use the returned clientId and clientSecret to implement the OAuth 2.0 authorization flow in your application.

Step 7: List and inspect your OAuth apps

Retrieve all OAuth Apps for your account to see their details.

curl -X https://manage.youriguide.com/api/v1/integrations/apps \
-H "X-Plntr-App-Id: $APP_ID" \
-H "X-Plntr-App-Token: $APP_TOKEN" \

Response:

[
{
"clientId": "660e8400-e29b-41d4-a716-446655440001",
"clientName": "My Web Application",
"description": "OAuth app for web application",
"redirectUris": ["https://myapp.com/oauth/callback"],
"logoUri": "https://myapp.com/logo.png",
"scope": ["iguide.list", "iguide.ro"],
"clientSecret": "sk_live_51H8F2KLbZqZ1X2Y3Z4W5V6U7T8S9R0Q1P2O3N4M5L6K7J8I9H0G1F2E3D4C5B6A",
"clientSecretHint": "...5B6A",
"clientIdIssuedAt": "2026-02-21T12:00:00Z",
"updatedAt": "2026-02-21T12:00:00Z"
},
{
"clientId": "fb63d3e5-2c7a-448c-9f4b-84c10a07dcc1",
"clientName": "My Other Application",
"description": "OAuth app for a different application",
"redirectUris": ["https://myapp.com/oauth/callback"],
"logoUri": "https://myapp.com/logo.png",
"scope": ["iguide.list", "iguide.ro"],
"clientSecret": "sk_live_XXXX",
"clientSecretHint": "...XXXX",
"clientIdIssuedAt": "2026-02-22T12:00:00Z",
"updatedAt": "2026-02-21T12:00:00Z"
}
]
Client Secret security

The full client secret values are never returned in list or get operations. Only the last 4 characters (clientSecretHint) are shown for identification purposes. This protects your credentials from being exposed in logs or API responses.

Step 8: Update an OAuth app

Update an existing OAuth application to change its name, description, redirect URIs, logo, or scopes.

curl -X PATCH https://manage.youriguide.com/api/v1/integrations/apps/660e8400-e29b-41d4-a716-446655440001 \
-H "X-Plntr-App-Id: $APP_ID" \
-H "X-Plntr-App-Token: $APP_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"clientName": "My Updated Application",
"scope": ["iguide.list", "iguide.ro", "iguide.rw"]
}'

Response:

{
"clientId": "660e8400-e29b-41d4-a716-446655440001",
"clientName": "My Updated Application",
"description": "OAuth app for web application",
"redirectUris": ["https://myapp.com/oauth/callback"],
"logoUri": "https://myapp.com/logo.png",
"scope": ["iguide.list", "iguide.ro", "iguide.rw"],
"clientSecretHint": "...def456",
"clientIdIssuedAt": "2026-02-21T12:00:00Z",
"updatedAt": "2026-02-23T10:30:00Z"
}
note

You can update any field except the client secret. To change the client secret, use the rotate endpoint in Step 8.

Step 9: Rotate an OAuth app secret

Generate a new client secret for an OAuth application when a secret has been compromised or as part of regular security rotation. Unlike API token rotation, OAuth secret rotation immediately invalidates the old secret with no grace period.

Immediate invalidation

Update your application with the new secret before rotating. There is no grace period, and the old secret will stop working immediately.

curl -X POST https://manage.youriguide.com/api/v1/integrations/apps/660e8400-e29b-41d4-a716-446655440001/rotate \
-H "X-Plntr-App-Id: $APP_ID" \
-H "X-Plntr-App-Token: $APP_TOKEN"

Response:

{
"clientId": "660e8400-e29b-41d4-a716-446655440001",
"clientName": "My Web Application",
"description": "OAuth app for web application",
"redirectUris": ["https://myapp.com/oauth/callback"],
"logoUri": "https://myapp.com/logo.png",
"scope": ["iguide.list", "iguide.ro"],
"clientSecret": "sk_live_NEW_SECRET_VALUE_HERE",
"clientSecretHint": "...HERE",
"clientIdIssuedAt": "2026-02-21T12:00:00Z",
"updatedAt": "2026-02-23T10:30:00Z"
}

Save the new clientSecret immediately and update your application to use it.

Step 10: Clean up unused credentials

Regularly audit and delete credentials you no longer use.

Delete an API token:

curl -X DELETE https://manage.youriguide.com/api/v1/integrations/tokens/550e8400-e29b-41d4-a716-446655440000 \
-H "X-Plntr-App-Id: $APP_ID" \
-H "X-Plntr-App-Token: $APP_TOKEN"

Response:

{
"ok": true
}

Delete an OAuth app:

curl -X DELETE https://manage.youriguide.com/api/v1/integrations/apps/660e8400-e29b-41d4-a716-446655440001 \
-H "X-Plntr-App-Id: $APP_ID" \
-H "X-Plntr-App-Token: $APP_TOKEN"

Response:

{
"ok": true
}
Deletion is permanent

Deleting credentials immediately invalidates them. Make sure you're not deleting credentials that are still in use.

Complete script

Here's a complete script that demonstrates all credential management operations:

#!/bin/bash
set -e

APP_ID="your-app-id"
APP_TOKEN="your-token"
BASE_URL="https://manage.youriguide.com/api/v1"

echo "=== API Credential Management Demo ==="
echo ""

echo "1. Creating API token..."
TOKEN_RESPONSE=$(curl -s -X POST "$BASE_URL/integrations/tokens" \
-H "X-Plntr-App-Id: $APP_ID" \
-H "X-Plntr-App-Token: $APP_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Demo Token",
"scopes": ["iguide.list", "iguide.ro"],
"expiresAt": "2028-02-21T12:00:00Z"
}')

TOKEN_ID=$(echo $TOKEN_RESPONSE | jq -r '.id')
NEW_TOKEN=$(echo $TOKEN_RESPONSE | jq -r '.token')
echo "✓ Token created: $TOKEN_ID"
echo ""

echo "2. Listing all tokens..."
curl -s "$BASE_URL/integrations/tokens" \
-H "X-Plntr-App-Id: $APP_ID" \
-H "X-Plntr-App-Token: $APP_TOKEN" \
| jq -r '.[] | "\(.name): \(.tokenHint)"'
echo ""

echo "3. Rotating token..."
ROTATE_RESPONSE=$(curl -s -X POST "$BASE_URL/integrations/tokens/$TOKEN_ID/rotate" \
-H "X-Plntr-App-Id: $APP_ID" \
-H "X-Plntr-App-Token: $APP_TOKEN")

ROTATED_TOKEN=$(echo $ROTATE_RESPONSE | jq -r '.token')
GRACE_PERIOD=$(echo $ROTATE_RESPONSE | jq -r '.previousToken.expiresAt')
echo "✓ Token rotated"
echo "Grace period ends: $GRACE_PERIOD"
echo ""

echo "4. Creating OAuth app..."
APP_RESPONSE=$(curl -s -X POST "$BASE_URL/integrations/apps" \
-H "X-Plntr-App-Id: $APP_ID" \
-H "X-Plntr-App-Token: $APP_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"clientName": "Demo OAuth App",
"redirectUris": ["https://localhost:3000/callback"],
"scope": ["iguide.list", "iguide.ro"]
}')

CLIENT_ID=$(echo $APP_RESPONSE | jq -r '.clientId')
CLIENT_SECRET=$(echo $APP_RESPONSE | jq -r '.clientSecret')
echo "✓ OAuth app created: $CLIENT_ID"
echo ""

echo "5. Listing all OAuth apps..."
curl -s "$BASE_URL/integrations/apps" \
-H "X-Plntr-App-Id: $APP_ID" \
-H "X-Plntr-App-Token: $APP_TOKEN" \
| jq -r '.[] | "\(.clientName): \(.clientSecretHint)"'
echo ""

echo "6. Updating OAuth app..."
curl -s -X PATCH "$BASE_URL/integrations/apps/$CLIENT_ID" \
-H "X-Plntr-App-Id: $APP_ID" \
-H "X-Plntr-App-Token: $APP_TOKEN" \
-H "Content-Type: application/json" \
-d '{"clientName": "Updated Demo App"}' \
| jq -r '"\(.clientName)"'
echo "✓ OAuth app updated"
echo ""

echo "7. Rotating OAuth app secret..."
curl -s -X POST "$BASE_URL/integrations/apps/$CLIENT_ID/rotate" \
-H "X-Plntr-App-Id: $APP_ID" \
-H "X-Plntr-App-Token: $APP_TOKEN" \
| jq -r '.clientSecretHint'
echo "✓ OAuth app secret rotated"
echo ""

echo "=== Demo complete ==="

Next steps

Congratulations! You can now create, rotate, and manage API credentials. Here's what to explore next:

Troubleshooting

Authentication fails when creating credentials

Problem: {"code": "unauthenticated"}

Solutions:

  • Verify your current credentials (App ID and Token) are correct
  • Check that the credentials have the credentials.manage scope
  • Ensure headers use exact names: X-Plntr-App-Id and X-Plntr-App-Token
  • Check for extra whitespace in your credentials

Reached maximum number of credentials

Problem: {"code": "resource_exhausted", "message": "Maximum number of API tokens reached"}

Solutions:

  • You can have a maximum of 10 API tokens per account
  • Delete unused tokens or rotate existing ones instead of creating new ones
  • Use a single OAuth app and rotate its secret if needed
  • See Step 9 for how to delete credentials

Lost or forgotten token value

Problem: You created a token but didn't save the value, and now it's not displayed

Solutions:

  • Token values are only shown once during creation
  • You cannot retrieve a lost token value
  • Rotate the token to get a new value (see Step 5)
  • The old value will remain valid during the grace period

OAuth secret rotation broke my application

Problem: After rotating an OAuth app secret, your application returns 401 Unauthorized

Solutions:

  • OAuth secret rotation immediately invalidates the old secret (no grace period)
  • Update your application configuration with the new secret immediately
  • If you can't access the new secret, delete the OAuth app and create a new one
  • Always test secret rotation in staging before production

Cannot update OAuth app fields

Problem: {"code": "invalid_argument", "message": "Invalid request body"}

Solutions:

  • Verify all URLs are valid (redirectUris, logoUri)
  • Check that scope names match available scopes (see Authentication Guide)
  • Ensure you're using the PATCH method for updates, not PUT
  • You cannot update the clientSecret via PATCH—use the rotate endpoint instead