skillZs
★ LIVE SKILL TAGS ★
>>> LIVE SKILLS INDEX <<<
* OPEN SOURCE *
NO LOGIN, NO TRACKING
※ REAL INSTALL DATA ※
← back to all skills
factorialco/factorial-code-skills302 installs

fcode-python

Write Python 3.13 for Factorial Code processes and modules — the main() entry point, fcode.context.parameters, fcode.import_module(), datastore/storage/env helpers, PEP 8 / snake_case, auto-installed dependencies, and return-value formats. Use when creating or editing .py process or module code for Factorial Code (fcode).

How do I install this agent skill?

npx skills add https://github.com/factorialco/factorial-code-skills --skill fcode-python
view source ↗

Is this agent skill safe to install?

  • Gen Agent Trust Hubpass

    The skill provides guidelines for developing Python 3.13 processes on the Factorial Code platform. It includes security best practices like secret encryption and environment variable usage. A minor attack surface exists for indirect prompt injection because the skill describes processing external data (parameters, files, and webhooks) without specifying sanitization protocols.

  • Socketpass

    No alerts

  • Snykpass

    Risk: LOW · No issues

What does this agent skill do?

Factorial Code — Python

Guidelines for writing Python that runs on Factorial Code. Runtime is Python 3.13. For the platform model (processes, modules, datastore) see fcode-core-concepts.

AspectGuideline
RuntimePython 3.13
Process entry filemain.py
Entry pointdef main()
Parametersfcode.context.parameters
Variablesos.getenv("X") or fcode.env.X
Import a modulefcode.import_module("module-slug")

Gotchas

  • Define main() as the entry point, but never call it yourself — Factorial Code invokes it.
  • fcode.import_module() names must be hardcoded string literals, never variables: fcode.import_module("shopify-client") ✅, fcode.import_module(name) ❌.
  • Never alias fcode.i18n — call it literally (fcode.i18n("key") ✅, t = fcode.i18n ❌): an aliased call throws "i18n is disabled" at runtime. See fcode-i18n.
  • Datastore stores only strings/numbers — json.dumps objects before set, json.loads after get.
  • Encrypt secrets you keep in the datastore — pass True as the third argument of set (fcode.datastore.set(key, token, True)); get decrypts transparently. Plain set stores the value in clear.
  • Use snake_case, not camelCase; follow PEP 8; add type hints where helpful.
  • Wrap the main flow in try/except, log the caught error with context via fcode-logs (see Logging), and raise actionable errors. Don't rely on global variables for state — pass it through parameters or return values.
  • Never hardcode or log secrets — read them from os.getenv.

Process template

def main():
    parameters = fcode.context.parameters

    # Your code here

    return { "message": "Success!" }

Helpers

import os

# Execution / process / schedule metadata
execution_id = fcode.execution.id
process_id = fcode.execution.process.id
schedule_id = fcode.execution.schedule.id   # when run from a schedule
timezone = fcode.execution.timezone

# Workspace (team) metadata
team_slug = fcode.team.slug

# Environment variables (secrets/config)
api_key = os.getenv("API_KEY")  # or fcode.env.API_KEY

# Import a Factorial Code module (hardcoded name only)
client = fcode.import_module("module-name")
client_v1 = fcode.import_module("module-name", "v1.0.0")  # pinned version tag or alias

# Run another process
fcode.processes.run("process-identifier", options)

# Translations (workspace locales — see fcode-i18n)
greeting = fcode.i18n("greetings.hello", {"name": "Ada"})  # %{name} filled in
fcode.i18n("greetings.hello", {"name": "Ada"}, {"locale": "es"})  # another locale, this lookup only (value may be dynamic)
locale = fcode.i18n.locale  # the execution's locale

Logging

Log through the shared fcode-logs module — level-gated logging inherited by every workspace. It reads the LOG_LEVEL team variable (debug | info | warn | error, default info) and forwards to print (stdout for debug/info, stderr for warn/error), so call sites read like a plain print:

log = fcode.import_module("fcode-logs")  # import once; reuse `log` everywhere, including `except`

log.info("sync started", process_slug)  # stdout when LOG_LEVEL ≤ info
log.debug(request_payload)               # stdout only when LOG_LEVEL=debug
log.warn("token missing — skipping")      # stderr when LOG_LEVEL ≤ warn
log.error("sync failed", str(err))         # stderr — always emitted

Be verbose — the logging policy (start/end, external calls and decisions at info; payloads at debug; always log inside except with context before re-raising) is in fcode-core-concepts §General rules. Set LOG_LEVEL=debug in a local or dev workspace to trace a full run; production stays at info. Never log secrets.

Dependencies

External pip packages install automatically — just import them. When the package name differs from the import name, declare it with @add-package:

# @add-package requests
import requests

Packages a parent workspace provides are already installed here — don't redeclare them (see fcode-core-concepts).

Datastore & storage

import json, os

# Datastore (strings/numbers only)
fcode.datastore.set("key", "value")
fcode.datastore.set("key", json.dumps({ "name": "John", "age": 30 }))
value = fcode.datastore.get("key")
fcode.datastore.delete("key")
# Encrypted at rest with a key owned by the workspace — for tokens, credentials,
# personal data. The third argument must be a bool; get() is transparent.
# Keys (entry names) are never encrypted.
fcode.datastore.set("oauth.token", access_token, True)
token = fcode.datastore.get("oauth.token")

# Storage (files)
local_path = os.path.join(os.environ.get("TMP_DATA_DIR"), "localfile.txt")

with open(local_path, "rb") as f:
    obj = fcode.storage.upload("path/myfile.txt", f)

objects = fcode.storage.list()

content = fcode.storage.download("path/myfile.txt")
with open(local_path, "wb") as f:
    f.write(content)

# Form file params arrive as "fcode.storage://…" references — strip the
# prefix before download; see fcode-forms.

# Signed download URL — { "url", "expiresAt" }. A real HTTPS link in the
# cloud, a file:// URL locally (same shape, no special-casing).
signed = fcode.storage.create_signed_url("path/myfile.txt")

fcode.storage.delete("path/myfile.txt")

Local disk: write temp files under os.environ.get("TMP_DATA_DIR").

Variables, schedules & OAuth

Read/write team variables and manage process schedules at runtime — scoped to your own team, no API token needed (like datastore/storage):

# Team variables (config/secrets)
# Default is SENSITIVE: fcode.variables.set(key, value) creates a sensitive
# (masked, immutable-sensitivity) variable. Pass sensitive=False for plain
# config values.
fcode.variables.set("API_KEY", "secret")  # sensitive by default
fcode.variables.set("BASE_URL", "https://api.acme.com", sensitive=False)  # required for non-secret config
v = fcode.variables.get("API_KEY")  # TeamVariable (has resolving_team_slug) or None
all_vars = fcode.variables.list()   # includes variables inherited from parents
fcode.variables.delete("API_KEY")   # no-op on an inherited variable

# Schedules (cron or one-off date_time) for a process
schedule = fcode.schedule.create(
    "my-process",
    cron="0 0 6 * * SUN",  # or: date_time="2026-04-24T12:30:00.000"
    parameters={"foo": "bar"},
    allow_concurrent_executions=False,  # optional
)
schedules = fcode.schedule.list(process_id=fcode.execution.process.id)
current = fcode.schedule.get(schedule["id"])
fcode.schedule.update(schedule["id"], cron="0 0 7 * * SUN")
fcode.schedule.pause(schedule["id"])
fcode.schedule.resume(schedule["id"])
fcode.schedule.delete(schedule["id"])
# delete every schedule for a process (pass the process UUID)
fcode.schedule.delete_for_process(fcode.execution.process.id)

# Start a third-party OAuth authorization. The platform holds the state, the PKCE
# verifier and the one redirect_uri registered with the provider, and invokes
# `on_complete` when the provider redirects back -- so never build an authorization
# URL or a state by hand, and never expose a callback webhook.
flow = fcode.oauth.start(
    authorize_url="https://login.example.com/authorize",
    client_id=fcode.env.PROVIDER_CLIENT_ID,
    scope=["openid", "offline_access"],
    on_complete="oauth-callback",      # the process that receives the code
    data={"companyId": company_id},    # carried back untouched; never a secret
    extra_params={"nonce": nonce},     # optional, provider-specific
    pkce=True,                         # default
)
flow.authorization_url  # hand this to the form's oauth widget

on_complete is invoked with code, codeVerifier, redirectUri, data and state in fcode.context.parameters (or error / errorDescription when the provider refused). Replay redirectUri in the token exchange — providers compare it byte for byte. See fcode-examples references/oauth-connect.md.

fcode.variables.set/delete only persist server-side; they are not reflected in fcode.env within the same run (fcode.env is a snapshot taken at start).

Inherited variables

list()/get() include variables inherited from parent workspaces (model in fcode-core-concepts); an inherited one carries resolving_team_slug naming its owner. set() on an inherited key creates an override in this workspace — the only way to change the value from here — and delete() on one is a silent no-op, so an uninstall process never removes a parent's credential (check resolving_team_slug if it must report what it actually removed). fcode.env.set_env_var / del_env_var behave the same way.

Sending email

Send email with the built-in fcode.send_mail — no SMTP setup required (model in fcode-core-concepts):

info = fcode.send_mail(
    to="user@example.com",   # string or list[str]
    subject="Report ready",
    text="Plain-text body",  # provide text, html, or both
    html="<b>HTML body</b>",
)
# info => { "messageId", "accepted", "rejected" }
  • The From address is fixed by the platform; a from you pass is ignored.
  • Each execution can send up to 3 emails by default; once the limit is reached, further calls throw.
  • Locally (fcode run) there is no manager, so the email is logged, not sent.
  • Acceptable use — who may be a recipient, what the body may carry — is a platform rule that blocks a release when broken; see fcode-core-concepts §Sending email.

Return values

# Standard
return { "message": "Success!" }

# Custom HTTP status (webhooks)
return { "status": 404, "body": { "message": "Not found" }, "headers": { "Content-Type": "application/json" } }

# Transient (not persisted in execution results)
return { "transient": True, "data": sensitive_data }

# Synchronous UI trigger button inside Factorial (see fcode-ui-triggers)
return { "data": { "synced": 42 } }
return { "errors": [{ "code": "missing_mapping", "message": "Map the Bonus concept first." }] }

Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.

<a href="https://skillzs.dev/skills/factorialco/factorial-code-skills/fcode-python">View fcode-python on skillZs</a>