Documents, mail, storage, and inputs in Python
Python uses the same configured connections and workspace permissions as other languages. Do not import independent database, S3, or IMAP clients. Read pinned workflow inputs rather than an arbitrary identifier of another email or run.
CSV, Excel, Word, and PDF
def run(ctx):
document = ctx.documents().from_url(ctx.param('file_url'), 'prices.csv')
if not document.complete():
ctx.fail('Document was not fully read')
if 'formulas_partly_calculated' in document.warnings():
ctx.fail('Specify a range and check formulas')
sheet = document.sheet(0)
for row in sheet.records(1):
sku = str(row.get('sku') or '')
if sku:
ctx.emit({'sku': sku, 'price': ctx.text().money(row.get('price'))}, sku)
The example expects CSV headers sku,price. Map other headers explicitly. Sheet indices start at zero; worksheet rows and cells start at one. Parse CSV as a document instead of splitting on commas: quoting, separators, and multiline values need a real parser.
An isolated document service parses the file; Python receives structured data rather than a filesystem path. Macros and external links do not execute. OCR and formulas have limits. Before exporting a complete report, inspect complete(), warnings(), and relevant formula errors. See Document and Worksheet.
Incoming email
def run(ctx):
mail = ctx.mail()
if not mail.available():
ctx.abort('This run has no incoming email')
for attachment in mail.attachments():
if not attachment.get('name', '').lower().endswith('.csv'):
continue
document = mail.document(attachment['id'])
if not document.complete():
ctx.fail('Attachment was not fully read')
for record in document.sheet(0).records(1):
sku = str(record.get('sku') or '')
if sku:
ctx.emit(record, sku)
A manual trial without a mail event does not gain an email automatically. Attachments must belong to the permitted event and still be retained. attachment() returns raw bytes; use document() for structured Excel, PDF, Word, and CSV extraction. See Mail.
Pinned workflow inputs and prices
sources().input('catalog') reads a configured input. Record inputs expose records, run_id, and collected_at. sources().document('supplier', id) reads an attachment from the pinned email input. compare_prices compares 2–10 sources locally without AI or currency conversion. Each offer needs sku, numeric price, and a shared currency; a repeated SKU within one source is an error.
Write a file to S3
def run(ctx):
stored = ctx.storage('media').put('reports/summary.txt', 'Ready', 'text/plain')
ctx.log('File saved', {'key': stored.key, 'dry_run': stored.dry_run})
The connection must be configured and permitted in advance. Read the final key from the returned StoredFile, rather than assuming it equals the suggested key. storage.url(key, ttl_seconds) produces an expiring signed URL. Trial writes may be dry runs; check the connection's behavior before depending on live side effects.
AI and offline agents
The Ai API provides analyze, transform, find, repair, document_rows, and extract. Apply the permitted data-handling policy before sending content to an external model. Document layout planning uses anonymous structure, but ordinary analyze/transform receive the supplied content. Cloud AI needs platform connectivity. Offline collection needs a tested local rule instead of a mandatory model call.