Skip to content

Keys & OAuth API

API endpoints for managing API keys and OAuth tokens.

List Keys

Get all stored API keys and OAuth tokens.

http
GET /keys

Example:

bash
curl "https://api.universalapi.co/keys" \
  -H "Authorization: Bearer $BEARER_TOKEN"

Response:

json
{
  "success": true,
  "data": {
    "userId": "user-id",
    "count": 2,
    "keys": [
      {
        "keyName": "openai",
        "keyValue": "sk-xxx...",
        "description": "OpenAI API key",
        "status": "active",
        "createdAt": 1736734800000
      },
      {
        "keyName": "google_oauth",
        "keyValue": "ya29.xxx...",
        "secretKeyName": "refresh_token",
        "secretKeyValue": "1//0xxx...",
        "description": "Google OAuth tokens",
        "status": "active",
        "additionalFields": {
          "token_type": "Bearer",
          "expires_at": 1736738400000,
          "scope": "https://www.googleapis.com/auth/drive"
        }
      }
    ]
  }
}

Create Key

Store a new API key.

http
POST /keys/create

Required Fields:

FieldTypeDescription
newKeystringKey name (e.g., "openai", "serpapi")
newKeyValuestringThe API key value

Optional Fields:

FieldTypeDescription
newKeyDescriptionstringDescription of the key
newSecretKeystringSecondary secret name
newSecretKeyValuestringSecondary secret value

Example:

bash
curl -X POST "https://api.universalapi.co/keys/create" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $BEARER_TOKEN" \
  -d '{
    "newKey": "openai",
    "newKeyValue": "sk-xxx...",
    "newKeyDescription": "OpenAI API key for GPT-4"
  }'

Response:

json
{
  "success": true,
  "data": {
    "message": "API key for openai created successfully",
    "keyInfo": {
      "keyName": "openai",
      "status": "active",
      "createdAt": 1736734800000
    }
  }
}

Delete Key

Remove a stored API key.

http
DELETE /keys/delete

Example:

bash
curl -X DELETE "https://api.universalapi.co/keys/delete" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $BEARER_TOKEN" \
  -d '{"keyName": "openai"}'

Alternative (query parameter):

bash
curl -X DELETE "https://api.universalapi.co/keys/delete?keyName=openai" \
  -H "Authorization: Bearer $BEARER_TOKEN"

OAuth Endpoints

Start OAuth Flow

Initiate OAuth authorization for a provider.

http
GET /oauth/{provider}/authorize

Supported Providers: google, microsoft, github

Query Parameters:

ParameterTypeDescription
userIdstringYour user ID
redirect_uristringWhere to redirect after auth

Example:

GET /oauth/google/authorize?userId=user-id&redirect_uri=https://universalapi.co/oauth/callback

Returns a URL to redirect the user to the provider's consent screen.


OAuth Callback

Handles the OAuth callback from the provider.

http
GET /oauth/{provider}/callback

This is called automatically by the OAuth provider. Do not call directly.


Refresh OAuth Token

Manually refresh an OAuth access token.

http
POST /oauth/{provider}/refresh

Body:

json
{
  "refreshToken": "1//0xxx..."
}

Example:

bash
curl -X POST "https://api.universalapi.co/oauth/google/refresh" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $BEARER_TOKEN" \
  -d '{"refreshToken": "1//0xxx..."}'

Response:

json
{
  "success": true,
  "data": {
    "message": "Token refreshed successfully",
    "provider": "google",
    "token_info": {
      "access_token_prefix": "ya29.a0...",
      "token_type": "Bearer",
      "expires_in": 3599,
      "expires_at": 1736738400000
    }
  }
}

Revoke OAuth Token

Revoke an OAuth token.

http
POST /oauth/{provider}/revoke

Body:

json
{
  "token": "ya29.xxx..."
}

Example:

bash
curl -X POST "https://api.universalapi.co/oauth/google/revoke" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $BEARER_TOKEN" \
  -d '{"token": "ya29.xxx..."}'

Access Tokens

Access tokens provide programmatic API access. There are two types:

User Tokens (uapi_ut_*)

Full-access tokens that inject all your global third-party keys at runtime.

Create User Token

http
POST /user/token/create
json
{
  "tokenName": "My App Token",
  "tokenType": "user",
  "description": "Token for my application",
  "creditLimit": 10000
}

Response:

json
{
  "data": {
    "tokenId": "tok-abc123",
    "token": "uapi_ut_abc123...",
    "tokenType": "user",
    "tokenName": "My App Token",
    "creditLimit": 10000,
    "creditUsed": 0
  }
}

Role Tokens (uapi_rt_*)

Scoped tokens that carry their own embedded key set. Only the attached keys are injected at runtime — not your global keys.

Create Role Token

http
POST /user/token/create
json
{
  "tokenName": "AWS Only",
  "tokenType": "role",
  "keys": {
    "aws_access_key_id": "AKIA...",
    "aws_secret_access_key": "wJal...",
    "aws_region": "us-east-1"
  }
}

Response:

json
{
  "data": {
    "tokenId": "tok-xyz789",
    "token": "uapi_rt_xyz789...",
    "tokenType": "role",
    "tokenName": "AWS Only",
    "keyCount": 3,
    "keyNames": ["aws_access_key_id", "aws_secret_access_key", "aws_region"]
  }
}

WARNING

Key values are never returned after creation. Only key names are visible in GET responses.

Update Role Token Keys

http
PUT /user/token/update
json
{
  "tokenId": "tok-xyz789",
  "keys": {
    "aws_access_key_id": "AKIA-new...",
    "aws_secret_access_key": "wJal-new...",
    "aws_region": "us-west-2"
  }
}

Common Token Operations

OperationEndpointMethod
CreatePOST /user/token/createtokenType: "user" or "role"
List allGET /user/token/listOptional ?type=role filter
Get detailsGET /user/token/{tokenId}Returns key names (not values) for role tokens
UpdatePUT /user/token/updateUpdate name, description, credit limit, or keys
RevokeDELETE /user/token/revokeBody: { "tokenId": "..." }
RegeneratePOST /user/token/{tokenId}/regenerateNew token value, same settings

Author Key Injection

Authors can attach a role token to their resources using the authorRoleToken field:

http
POST /mcp-server/create
json
{
  "serverName": "my-textract-server",
  "sourceCode": "...",
  "authorRoleToken": "uapi_rt_my_role_token",
  "authorPricing": { "pricePerInvocation": 0.05 }
}

At runtime, the author's role token keys are resolved and merged with the invoking user's keys. The invoking user's keys take priority on conflicts.


Using Keys at Runtime

When you execute any resource (action, agent, or MCP server), your stored API keys are automatically injected into the runtime environment. This means resource authors can write code that uses the invoking user's keys without any manual configuration.

Key Structure

Each key is a dict with these fields:

FieldTypeDescription
keyNamestringThe key name (e.g., "openai")
keyValuestringThe primary key value
descriptionstringDescription of the key
secretKeyNamestringSecondary secret name (optional)
secretKeyValuestringSecondary secret value (optional)
additionalFieldsdictExtra metadata (optional)

In Actions

Action code receives keys via parameters['keys']:

python
def handler(parameters):
    # Check if key exists
    keys = parameters.get('keys', {})
    if 'openai' not in keys:
        return {"error": "OpenAI key not configured. Add it in Credentials → Third-Party Keys."}

    # Access the key
    api_key = keys['openai']['keyValue']

    # Use it
    import json
    from urllib.request import Request, urlopen
    req = Request(
        "https://api.openai.com/v1/chat/completions",
        headers={
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json"
        },
        data=json.dumps({
            "model": "gpt-4",
            "messages": [{"role": "user", "content": "Hello!"}]
        }).encode()
    )
    response = json.loads(urlopen(req).read())
    return {"result": response}

In Strands Agents

Agent code can access keys in two ways:

1. user_keys dict (injected into sandbox namespace):

python
from strands import Agent, tool
from strands.models import BedrockModel

@tool
def call_openai(prompt: str) -> str:
    """Call OpenAI API using the user's stored API key."""
    import json
    from urllib.request import Request, urlopen
    
    # user_keys is automatically available in the agent namespace
    api_key = user_keys.get('openai', {}).get('keyValue')
    if not api_key:
        return "Error: No OpenAI key found. Add it in Credentials → Third-Party Keys."
    
    req = Request(
        "https://api.openai.com/v1/chat/completions",
        headers={
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json"
        },
        data=json.dumps({
            "model": "gpt-4",
            "messages": [{"role": "user", "content": prompt}]
        }).encode()
    )
    return json.loads(urlopen(req).read())['choices'][0]['message']['content']

def create_agent():
    agent = Agent(
        model=BedrockModel(model_id="us.anthropic.claude-sonnet-4-20250514-v1:0"),
        tools=[call_openai],
        system_prompt="You can call OpenAI models using the user's API key."
    )
    return agent, []

2. UAPI_KEYS_JSON environment variable (JSON-serialized full keys dict):

python
import os, json

keys = json.loads(os.environ.get('UAPI_KEYS_JSON', '{}'))
openai_key = keys.get('openai', {}).get('keyValue')
stripe_key = keys.get('stripe', {}).get('keyValue')

In MCP Servers

MCP server source code receives the full user context (including keys) via the userContext parameter passed to createMcpServer():

javascript
function createMcpServer(userContext) {
    const keys = userContext?.keys || {};
    const openaiKey = keys.openai?.keyValue;
    
    // Use keys in tool implementations
    server.tool("call-openai", { prompt: z.string() }, async ({ prompt }) => {
        const response = await fetch("https://api.openai.com/v1/chat/completions", {
            method: "POST",
            headers: {
                "Authorization": `Bearer ${openaiKey}`,
                "Content-Type": "application/json"
            },
            body: JSON.stringify({
                model: "gpt-4",
                messages: [{ role: "user", content: prompt }]
            })
        });
        return { content: [{ type: "text", text: JSON.stringify(await response.json()) }] };
    });
}

OAuth Token Structure

When Google OAuth is connected:

python
keys["google_oauth"] = {
    "keyName": "google_oauth",
    "keyValue": "ya29.xxx...",  # Access token
    "secretKeyName": "refresh_token",
    "secretKeyValue": "1//0xxx...",  # Refresh token
    "additionalFields": {
        "token_type": "Bearer",
        "expires_at": 1736738400000,
        "scope": "https://www.googleapis.com/auth/drive"
    }
}

Automatic Token Refresh

Universal API automatically refreshes OAuth tokens when they're within 5 minutes of expiration. Your actions and agents always receive valid access tokens.

Summary: Keys Injection by Runtime

RuntimeHow Keys Are AccessedExample
Actions (Python)parameters['keys']parameters['keys']['openai']['keyValue']
Agents (Python)user_keys dict or UAPI_KEYS_JSON env varuser_keys['openai']['keyValue']
MCP Servers (JS)userContext.keysuserContext.keys.openai.keyValue

Universal API - The agentic entry point to the universe of APIs