Skip to main content

Python SDK for the SmartLink workflow API and webhook verification.

Project description

SmartLink SDK

Table of Contents

Introduction

The SmartLink SDK allows you to make requests to the SmartLink API. To use the SDK, you need to obtain an API key from your SmartLink account via the Workflow administrator menu.

Examples

Example 1: Init client locally

from smartlink_sdk import SmartLinkClient, getApps

def fetch():
    client = SmartLinkClient(
        base_url="https://<your_smartlink_url>/api/workflow",
        api_key="<your_secret_api_key>",
    )
    apps = getApps({"client": client})
    print("apps", apps.data)

fetch()

Example 2: Configure client globally

from smartlink_sdk import client, getApps

client.set_config(
    base_url="https://<your_smartlink_url>/api/workflow",
    headers={
        "x-api-key": "<your_secret_api_key>",
    },
)

def fetch():
    apps = getApps()
    print("apps", apps.data)

fetch()

Testing

The SDK ships with end-to-end Jest tests that expect a local SmartLink API.

Create ts/.env with:

API_BASE_URL=http://localhost:3003/api/workflow
API_KEY=<your api key>

Then run:

npm run test

SDK Functions

getAppByClientId

Fetches an app by its unique ID.

getAppByClientId({
    "path": {
        "clientId": "app_id",
    },
})

Parameters

Parameters type required default
clientId string true

Response

200: { data: The App }

401: { error: "Not authorized" }

404: { error: "App not found" }

500: { error: "Internal server error" }

postApp

Creates a new app for your organization

postApp({
    "body": {
        "title": "App title",
        "url": "https://app.com",
        "iconUrl": "https://app.com/icon.png",
        "description": "App description",
        "slug": "my_unique_slug",
    },
})

Body

Parameters type required default
title string true
url string false null
iconUrl string false null (icons are automatically retrieved by smartlink)
description string false null
slug string false auto generated (randomUUID)

Response

200: { data: The added App }

401: { error: "Not authorized" }

500: { error: "Internal server error" }

putAppByClientId

Updates an app by its client ID.

putAppByClientId({
    "path": {
        "clientId": "your_client_id",
    },
    "body": {
        "title": "Updated title",
        "url": "https://updated-url.com",
        "iconUrl": "https://updated-url.com/icon.png",
        "description": "Updated description",
        "slug": "updated_slug",
    },
})

Parameters

Parameters type required default
clientId string true

Body

Parameters type required default
title string true
url string false null
iconUrl string false null
description string false null
slug string false null

Response

200: { data: The updated App }

401: { error: "Not authorized" }

404: { error: "App not found" }

500: { error: "Internal server error" }

deleteAppByClientId

Deletes an app by its client ID.

deleteAppByClientId({
    "path": {
        "clientId": "your_client_id",
    },
})

Parameters

Parameters type required default
clientId string true

Response

200: { data: The deleted App }

401: { error: "Not authorized" }

404: { error: "App not found" }

500: { error: "Internal server error" }

getApps

Get all apps of your organization.

apps = getApps()

Response

200: { data: Array of created Apps }

401: { error: "Not authorized" }

500: { error: "Internal server error" }

postApps

Creates multiple apps for your organization.

Note: this endpoint is defined by the SDK contract, but the current backend returns an Internal server error in this environment. For now, prefer calling postApp multiple times when you need to create several apps.

postApps({
    "body": {
        "apps": [
            {
                "title": "App title 1",
                "url": "https://app1.com",
                "iconUrl": "https://app1.com/icon.png",
                "description": "App description 1",
                "slug": "unique_slug_1",
            },
            {
                "title": "App title 2",
                "url": "https://app2.com",
                "iconUrl": "https://app2.com/icon.png",
                "description": "App description 2",
                "slug": "unique_slug_2",
            },
        ],
    },
})

Body

Array of:

Parameters type required default
title string true
url string false null
iconUrl string false null
description string false null
slug string false null

Response

200: { data: Array of created Apps }

401: { error: "Not authorized" }

500: { error: "Internal server error" }

getEvents

Retrieve log events with various filters.

events = getEvents({
    "query": {
        "fromDate": 1733995830,
        "toDate": 1733995840,
        "type": 0,
        "membershipId": 1,
        "folderId": 1,
        "userId": 1,
        "deviceId": 1,
        "appClientId": "1",
    },
})

Query

Parameters type required default
fromDate integer false null
toDate integer false null
type integer (see Events type) false null
membershipId integer false null
folderId integer false null
userId integer false null
deviceId integer false null
appClientId string false null

Events type

type description
0 An app was opened
1 Someone is successfully connected on SmartLink
2 A connection has failed on SmartLink
3 A connection was denied on SmartLink
4 Someone is successfully connected from an extension
5 A connection from an extension has failed
6 A connection from an extension was denied
7 A new password was set
8 A login from was blocked by anti-phishing feature

Response

200: { data: Array of events }

401: { error: "Not authorized" }

500: { error: "Internal server error" }

getFolderById

Fetches a folder by its ID.

getFolderById({
    "path": {
        "id": "your_folder_id",
    },
})

Parameters

Parameters type required default
id string true

Response

200: { data: The Folder }

401: { error: "Not authorized" }

404: { error: "Folder not found" }

500: { error: "Internal server error" }

deleteFolderById

Deletes a folder resource identified by the given ID.

deleteFolderById({
    "path": {
        "id": "your_folder_id",
    },
})

Parameters

Parameters type required default
id string true

Response

200: { data: The deleted Folder }

401: { error: "Not authorized" }

404: { error: "Folder not found" }

500: { error: "Internal server error" }

postFolder

Creates a new folder within your organization.

postFolder({
    "body": {
        "name": "Folder name",
        "parentId": 2,
    },
})

Body

Parameters type required default
name string true
parentId integer false null

Response

200: { data: The created Folder }

401: { error: "Not authorized" }

500: { error: "Internal server error" }

getFolders

Get all folders of your organization.

folders = getFolders()

Response

200: { data: Array of Folder }

401: { error: "Not authorized" }

500: { error: "Internal server error" }

postImportApps

Import multiple apps with folders, users assignment and configuration.

postImportApps({
    "body": {
        "apps": [
            {
                "title": "Imported app",
                "url": "https://app.example.com",
                "type": "SMARTLINK",
                "folders": ["/Sales/"],
            },
        ],
    },
})

Body

Parameters type required default
apps array false null

Response

200: { data: { count: number } }

401: { error: "Not authorized" }

500: { error: "Internal server error" }

postImportMemberships

Import multiple users/memberships with folders assignment and registration.

postImportMemberships({
    "body": {
        "users": [
            {
                "firstName": "John",
                "name": "Doe",
                "email": "john.doe@example.com",
                "folders": ["/Sales/"],
            },
        ],
    },
})

Body

Parameters type required default
users array false null

Response

200: { data: { count: number } }

401: { error: "Not authorized" }

500: { error: "Internal server error" }

getFolderByIdApps

Get all apps in a specific folder with pagination, search and filtering.

apps = getFolderByIdApps({
    "path": {
        "id": 1,
    },
    "query": {
        "page": 1,
        "pageSize": 10,
    },
})

Parameters

Parameters type required default
id integer true

Query

Parameters type required default
page integer false null
pageSize integer false null
provisioningFilter string false null
search string false null
sortBy string false null
sortOrder string false null
typeFilter string false null

Response

200: { data: { apps: Array of App; total: number } }

401: { error: "Not authorized" }

500: { error: "Internal server error" }

getFolderByIdMemberships

Get all memberships that have access to a specific folder with pagination, search and filtering.

memberships = getFolderByIdMemberships({
    "path": {
        "id": 1,
    },
    "query": {
        "page": 1,
        "pageSize": 10,
    },
})

Parameters

Parameters type required default
id integer true

Query

Parameters type required default
page integer false null
pageSize integer false null
roleFilter string false null
search string false null
sortBy string false null
sortOrder string false null
stateFilter string false null
statusFilter string false null

Response

200: { data: { memberships: Array of User; total: number } }

401: { error: "Not authorized" }

500: { error: "Internal server error" }

postMembershipByIdDeactivate

Updates a membership's status to "INACTIVE" by its ID.

postMembershipByIdDeactivate({
    "path": {
        "id": "your_membership_id",
    },
})

Parameters

Parameters type required default
id string true

Response

200: { data: The updated User }

401: { error: "Not authorized" }

404: { error: "User not found" }

500: { error: "Internal server error" }

postMembershipByIdRegister

Generate a registration link for an existing membership and optionally send an invitation email.

postMembershipByIdRegister({
    "path": {
        "id": 1,
    },
    "body": {
        "sendMail": True,
    },
})

Parameters

Parameters type required default
id integer true

Body

Parameters type required default
sendMail boolean false false

Response

200: { data: { registerLink: string; mailSent: boolean } }

401: { error: "Not authorized" }

404: { error: "User not found" }

500: { error: "Internal server error" }

getMembershipById

Fetches a membership by its ID.

getMembershipById({
    "path": {
        "id": "your_membership_id",
    },
})

Parameters

Parameters type required default
id string true

Response

200: { data: The User }

401: { error: "Not authorized" }

404: { error: "User not found" }

500: { error: "Internal server error" }

putMembershipById

Updates a membership by its ID.

putMembershipById({
    "path": {
        "id": "<your_membership_id>",
    },
    "body": {
        "firstName": "John",
        "name": "Doe",
        "phone": "+336...",
        "email": "john.doe@fake.com",
        "role": "ADMIN",  # or "USER"
        "status": "ACTIVE",  # or "INACTIVE"
        "language": "fr",
    },
})

Parameters

Parameters type required default
id string true

Body

Parameters type required default
firstName string false keep actual value
name string false keep actual value
phone string false keep actual value
email string false keep actual value
role "ADMIN" or "USER" false keep actual value
status "ACTIVE" or "INACTIVE" false keep actual value
language string ("fr", "en", "es", "zh", "de") false keep actual value

Response

200: { data: The updated User }

401: { error: "Not authorized" }

404: { error: "User not found" }

500: { error: "Internal server error" }

deleteMembershipById

Deletes a membership resource identified by the given ID.

deleteMembershipById({
    "path": {
        "id": "<your_membership_id>",
    },
})

Parameters

Parameters type required default
id integer true

Response

200: { data: The deleted User }

401: { error: "Not authorized" }

404: { error: "User not found" }

500: { error: "Internal server error" }

postMembership

Creates a new membership for your organization.

postMembership({
    "body": {
        "firstName": "John",
        "name": "Doe",
        "phone": "+336...",
        "email": "john.doe@fake.com",
        "sendMail": True,  # invite user by email
        "sender": {
            # sender will be displayed in the invitation email
            "firstName": "Admin",
            "name": "istrator",
        },
        "isAdmin": False,  # administrator membership?
    },
})

Body

Parameters type required default
firstName string true
name string true
email string true
phone string true null
sendMail boolean false false
sender {firsName: string; name: string} false null
isAdmin boolean false false

Response

200: { data: The created User }

401: { error: "Not authorized" }

500: { error: "Internal server error" }

getMemberships

Get all memberships of your organization.

memberships = getMemberships()

Response

200: { data: Array of User }

401: { error: "Not authorized" }

500: { error: "Internal server error" }

postMemberships

Creates multiple memberships for your organization.

postMemberships({
    "body": [
        {
            "firstName": "John",
            "name": "Doe",
            "phone": "+336...",
            "email": "john.doe@fake.com",
            "sendMail": True,  # invite user by email
            "sender": {
                # sender will be displayed in the invitation email
                "firstName": "Admin",
                "name": "istrator",
            },
            "isAdmin": False,  # administrator membership?,
        },
        {
            "firstName": "Jane",
            "name": "Doe",
            "phone": "+336...",
            "email": "jane.doe@fake.com",
            "sendMail": False,
            "isAdmin": True,  # administrator membership?
        },
    ],
})

Body

Array of:

Parameters type required default
firstName string true
name string true
email string true
phone string true null
sendMail boolean false false
sender {firsName: string; name: string} false null
isAdmin boolean false false

Response

200: { data: {memberships: Array of created Users, count: number (total of users for this search)} }

401: { error: "Not authorized" }

500: { error: "Internal server error" }

getMembershipsSearch

Search memberships with various filters. Responses are paginated so you need to specify the page you want.

memberships = getMembershipsSearch({
    "query": {
        "page": 1,
        "pageSize": 10,
        "roles": "ADMIN,USER",
        "search": "john",  # search in names, firstNames, emails and phones
        "statusFilter": "all",
    },
})

Query

Parameters type required default
page integer true
pageSize integer false 10
roles string (comma separated ) false null (all roles)
search string false null
statusFilter string ("all", "registered" or "unregistered") false all

Response

200: { data: Array of User }

401: { error: "Not authorized" }

500: { error: "Internal server error" }

getOrganization

Retrieve public information about the organization associated with the workflow API key.

organization = getOrganization()

Response

200: { data: Organization }

401: { error: "Not authorized" }

500: { error: "Internal server error" }

Schemas

User

Field type description
id integer Membership unique ID
role string Membership role (OWNER, USER, ADMIN)
organizationId integer Unique ID of the organization
userId integer Unique ID of the user
createdAt Date Creation date
updatedAt Date Last update date
status string Membership status (ACTIVE, INACTIVE)
language string Preferred language (ex 'en')
name string User's name
firstName string User's first name
email string User's email address
phone string User's phone number

Folder

Field type description
id integer Folder unique ID
name string Folder name
forAllUsers boolean Indicates if folder is for all users
organizationId integer Unique ID of the organization
parentId integer Unique ID of the parent folder
path string Full path of the folder (/NewYork/HR)

App

Field type description
clientId string App client ID
type string App type (SMARTLINK, SAML2)
title string App title
url string App URL
iconUrl string App icon URL
ping integer How many times the app was opened
slug string Unique slug for the app
description string App description
organizationId integer Unique ID of the organization

Organization

Field type description
id integer Organization unique ID
name string Organization name
maxUser integer Maximum number of users
maxApp integer Maximum number of apps
maxDomain integer Maximum number of domains
maxFolder integer Maximum number of folders
createdAt Date Creation date
updatedAt Date Last update date
admin boolean Organization is admin managed
allowCreation boolean Allow creation
createOnly boolean Create only
licenseExpiration Date License expiration date
stripeCustomerId string Stripe customer ID
language string Default language
license string License type (FREE, PRO, ENTERPRISE)
visibility string Visibility (PUBLIC, PRIVATE)

Event

Field type description
id integer Log event unique ID
createdAt DateTime Creation date of the log event
message string Log event message
type integer Log event type type
organizationId integer Unique ID of the organization (nullable)
membershipId integer Unique ID of the membership (nullable)
folderId integer Unique ID of the folder (nullable)
deviceId integer Unique ID of the device (nullable)
userId integer Unique ID of the user (nullable)
appClientId string Unique client ID of the app (nullable)

Webhooks

Verify

import asyncio
from smartlink_sdk import Webhook

async def handle_webhook():
    body = await request.json()  # assuming request is an async request object
    if await Webhook.verify(body, "<YOUR API KEY>"):
        if body["event"] == "membership.created":
            # handle membership created webhook
            # ...
        # ....
    else:
        # wrong signature or timestamp expired
        pass

asyncio.run(handle_webhook())

Webhook Types

The following webhook types are available:

  • Membership Webhooks

    • membership.created: Triggered when a membership is created.
    • membership.deleted: Triggered when a membership is deleted.
    • membership.updated: Triggered when a membership is updated.
    • membership.connected: Triggered when a membership connection is established.
    • membership.connected.fail: Triggered when a membership connection fails.
    • membership.connected.denied: Triggered when a membership connection is denied.
    • membership.extension.connected: Triggered when a membership is connected from an extension.
    • membership.extension.connected.fail: Triggered when a membership connection fails from an extension.
    • membership.extension.connected.denied: Triggered when a membership connection is denied from an extension.
  • Folder Webhooks

    • folder.created: Triggered when a folder is created.
    • folder.deleted: Triggered when a folder is deleted.
    • folder.updated: Triggered when a folder is updated.
  • App Webhooks

    • app.created: Triggered when an app is created.
    • app.deleted: Triggered when an app is deleted.
    • app.updated: Triggered when an app is updated.
    • app.opened: Triggered when an app is opened.
  • Event Webhooks

    • event: Triggered when a log event occurs.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

smartlink_sdk-0.1.0.tar.gz (22.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

smartlink_sdk-0.1.0-py3-none-any.whl (15.4 kB view details)

Uploaded Python 3

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page