Example Tools using Outbound Apps

Here are examples of how backend services can use Descope Outbound Apps to retrieve user-specific tokens for third-party platforms like Salesforce, HubSpot, and Google Calendar.

Each example assumes the user has already connected via the outbound app connect flow, either using the Frontend SDKs or Descope Flows.

Fetching Outbound App Tokens

To use tokens from your backend, you'll need to fetch them using the Descope Management API. Below are examples in different languages:

Node.js / JavaScript

// Node.js example using fetch API
async function getOutboundToken(appId, userId, scopes = []) {
  const projectId = process.env.DESCOPE_PROJECT_ID;
  const managementKey = process.env.DESCOPE_MANAGEMENT_KEY;
  
  const response = await fetch('https://api.descope.com/v1/mgmt/outbound/app/user/token', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${projectId}:${managementKey}`
    },
    body: JSON.stringify({
      appId,
      userId,
      scopes,
      options: {
        withRefreshToken: false,
        forceRefresh: false
      }
    })
  });
  
  if (!response.ok) {
    throw new Error(`Failed to fetch token: ${response.status} ${response.statusText}`);
  }
  
  const data = await response.json();
  return data.token;
}

Python

import requests
 
def get_outbound_token(app_id, user_id, scopes=None):
    project_id = "YOUR_PROJECT_ID"
    management_key = "YOUR_MANAGEMENT_KEY"
    
    url = "https://api.descope.com/v1/mgmt/outbound/app/user/token"
    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {project_id}:{management_key}"
    }
    
    payload = {
        "appId": app_id,
        "userId": user_id,
        "scopes": scopes or [],
        "options": {
            "withRefreshToken": False,
            "forceRefresh": False
        }
    }
    
    response = requests.post(url, headers=headers, json=payload)
    
    if response.status_code != 200:
        raise Exception(f"Failed to fetch token: {response.status_code} {response.text}")
    
    data = response.json()
    return data["token"]

🔗 Salesforce Opportunities Tool

Use Descope Outbound Apps to fetch Salesforce access tokens, then query opportunities from the Salesforce API.

// Fetch the token from Descope Management API
const token = await getOutboundToken("salesforce", "user-123", ["full"]);
const accessToken = token.accessToken;
 
const instanceUrl = process.env.SALESFORCE_INSTANCE_URL;
const query = `SELECT Id, Name, StageName, CloseDate FROM Opportunity LIMIT 5`;
const endpoint = `${instanceUrl}/services/data/v57.0/query/?q=${encodeURIComponent(query)}`;
 
const response = await fetch(endpoint, {
  headers: { 'Authorization': `Bearer ${accessToken}` }
});
const data = await response.json();

📅 Google Calendar Events Tool

Fetch a user's calendar events with Descope-managed tokens:

// Fetch the token from Descope Management API
const token = await getOutboundToken(
  "google-calendar", 
  "<Descope User ID>", 
  ["https://www.googleapis.com/auth/calendar"]
);
const accessToken = token.accessToken;
 
const now = new Date().toISOString();
const url = `https://www.googleapis.com/calendar/v3/calendars/primary/events?timeMin=${now}&maxResults=5&orderBy=startTime&singleEvents=true`;
 
const response = await fetch(url, {
  headers: { 'Authorization': `Bearer ${accessToken}` }
});
const events = await response.json();

📇 HubSpot Contacts Tool (Python Example)

Use the Outbound App token to get CRM contact records from HubSpot:

# Fetch the token from Descope Management API
token = get_outbound_token("hubspot", "user-123", ["contacts"])
access_token = token["accessToken"]
 
import requests
 
url = "https://api.hubapi.com/crm/v3/objects/contacts?limit=10"
headers = {
    "Authorization": f"Bearer {access_token}",
    "Content-Type": "application/json"
}
 
response = requests.get(url, headers=headers)
contacts = response.json()

🛠️ Slack Bot + User Token Example

Use Descope Outbound Apps to fetch both Slack bot and user tokens to build bots that can also perform user-authorized actions:

What You Receive from Descope

When you call:

const token = await getOutboundToken(
  "slack",
  "<Descope User ID>",
  ["chat:write"],
  { userScopes: ["channels:history"] }
);

Descope will return:

{
  "accessToken": "<slack-bot-token>",
  "expiresAt": 1712345678,
  "refreshToken": "<optional-refresh-token>",
  "scopes": ["chat:write"],
  "userToken": "<slack-user-token>",
  "userScopes": ["channels:history"],
  "provider": "slack"
}
  • accessToken: Slack Bot User OAuth token, scoped to the bot-level permissions you requested (e.g., chat:write).
  • userToken: Slack User OAuth token, scoped to the user-level permissions you requested (e.g., channels:history).
  • scopes / userScopes: Reflect the scopes granted during the outbound app OAuth exchange.
  • refreshToken: Present only if your Slack app and Descope outbound app are configured for refresh tokens.
  • expiresAt: Unix timestamp for expiry, based on Slack’s token behavior.
  • provider: Always "slack" for clarity.

Example: Posting message as the bot, Reading User Channels

 
// Fetch Slack bot + user tokens
const token = await getOutboundToken(
  "slack", 
  "<Descope User ID>", 
  ["chat:write"], 
  { userScopes: ["channels:history"] }
);
 
// Post a message as the bot
const postResponse = await fetch("https://slack.com/api/chat.postMessage", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${token.accessToken}`,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    channel: "<channel-id>",
    text: "Hello from your Slack bot!"
  })
});
 
// Read user channels using the user token
const channelsResponse = await fetch("https://slack.com/api/conversations.list", {
  headers: {
    "Authorization": `Bearer ${token.userToken}`
  }
});
const channelsData = await channelsResponse.json();
console.log(channelsData);

With this flow, you can easily combine bot functionality (posting messages, responding to events) with user-authorized actions (fetching channels, reading conversations, posting as user with additional scopes) within your Slack-integrated flows using Descope Outbound Apps.

Important Notes

  • Store your Management Key securely - it has admin permissions to your Descope project
  • Use environment variables to manage your Project ID and Management Key
  • Token requests should only occur server-side, never in client code
  • Set appropriate scopes when requesting tokens for better security

Coming soon: More examples for platforms like Slack, Notion, Zoom, and GitHub!

Was this helpful?