Blog
Guides

How to share a Python script that runs as each user

DK
Dan Kowalsky
Founder ·

You wrote a script that cleans up your inbox: it finds the newsletters you never read, unsubscribes and archives the backlog. Your teammates want it too. The problem is that it's your inbox. To run it for them, you'd need their Gmail access, and the usual ways of getting that are bad: their password, an app password they generate, or a token they paste into a chat.

This guide covers the difference between scripts that run as a shared account and scripts that run as each user, and how to share the second kind safely.

Two kinds of scripts

Runs as a shared accountRuns as each user
ExampleRefund an order in Stripe, export signups from the databaseClean up my inbox, summarize my calendar, file my receipts
CredentialsOne company keyEach person's own account
In CallablesA secret you set onceA connection each person makes
If it's missingThe run starts without it, and builders see a warningThe run doesn't start, and the person is asked to connect

Most internal tools are the first kind. But once you start automating personal work, email, calendars and documents, you need the second, and that's where scripts get hard to share.

Declare a connection

A connection says "this function uses the account of whoever runs it". Declare it once and pass it to the functions that need it:

import requests

from callables import Connection, Group

google = Connection("google", provider="google", scopes=["gmail.modify"])
gmail = Group("gmail", title="Gmail", connections=[google])


@gmail.deploy(title="Recent senders")
def recent_senders(days: int = 7) -> list[dict]:
    """Who emailed you the most lately."""
    headers = {"Authorization": f"Bearer {google.token}"}
    ...

google.token is an access token for the person running the function. When Alice runs it, it's Alice's. When Bob runs it, it's Bob's. Your code is the same either way, and nobody's credentials ever appear in it.

You bring the OAuth app, so Google's sign-in screen shows your app's name and exactly what it asks for. See creating a Google OAuth app. Callables handles everything around it: the sign-in, storing and refreshing each person's tokens, and handing the right token to each run.

What your teammates see

The first time someone opens the function, they see a Connect Google button where Run would be. It opens Google's own sign-in page. They choose their account and allow access, they're sent back, and they can run it.

From then on it just works:

  • Other functions in the project that use the same connection run right away.
  • If you add a scope, their next run asks them to reconnect and grant it.
  • If they untick a permission on Google's screen, the connection is refused with a message naming what's missing, instead of failing halfway through a run.
  • The Connections page in the sidebar lists everything they've connected. Disconnect deletes their tokens from Callables and revokes them with Google.

Whose account a run uses

How the run startedAccount it uses
Someone runs it from the formTheirs
A scheduleThe person who made the schedule

A run never borrows someone else's connection. If Bob schedules "archive newsletters every Monday", it runs against Bob's inbox only.

Who can see what

This is the part to get right when you share access to people's accounts:

  • Builders never see tokens in the web app, and tokens are hidden in run logs, like secrets.
  • Tokens are encrypted at rest, and only the API and the run workers decrypt them.
  • Your code does receive the token during a run, because it has to call Gmail. Someone running your function is trusting you with their account, the same as installing any app. So ask for the narrowest scope that works: gmail.readonly if you only read.
  • Each run is its own process, so one person's token can't leak into another person's run.

Mixing both

Functions often need both kinds: their own inbox plus a company key. Declare both, and each comes from the right place:

@gmail.deploy(title="Log my customer emails to the CRM", secrets=["CRM_TOKEN"])
def log_customer_emails(days: int = 1) -> str:
    """Copies your recent emails with customers into the CRM."""
    ...  # google.token is yours; os.environ["CRM_TOKEN"] is the company's

Try it

The Gmail examples in the cookbook are a complete project: inbox statistics, recent senders, archive a sender, and finding mailing lists with one-click unsubscribe. Test it on your own machine with call connect, then push and share it with your team.

Turn your scripts into tools your team can run

Add @deploy to a Python function and push. Your team gets a form, schedules and run history.

See how it works

Keep reading