UIFORM_API_KEY=sk_xxxxxxxxx # Create your API key on https://www.uiform.com
Then, as we will use your API key to make requests to OpenAI on your behalf within a processor, you need to store your API key in the UiForm secrets manager:
We use a standard JSON Schema with custom annotations (X-SystemPrompt and X-ReasoningPrompt) as a prompt-engineering framework for the extraction process.
These annotations help guide the LLM’s behavior and improve extraction accuracy.
You can learn more about these in our JSON Schema documentation.
Copy
from uiform import UiFormfrom openai import OpenAIfrom pydantic import BaseModel, Field, ConfigDict# Define your extraction schemaclass Invoice(BaseModel): model_config = ConfigDict( json_schema_extra = { "X-SystemPrompt": "You are an expert at analyzing invoice documents." } ) total_amount: float = Field(..., description="The total invoice amount", ) date: str = Field(..., description="Invoice date in YYYY-MM-DD format", json_schema_extra={ "X-ReasoningPrompt": "Look for dates labeled as 'Invoice Date', 'Date', etc." } )# Process document and extract datauiclient = UiForm()doc_msg = uiclient.documents.create_messages( document = "invoice.pdf")schema_obj = uiclient.schemas.load( pydantic_model = Invoice)# Extract structured data with any LLMclient = OpenAI()completion = client.beta.chat.completions.parse( model="gpt-4o", messages=schema_obj.openai_messages + doc_msg.openai_messages, response_format=schema_obj.inference_pydantic_model)print("Extracted data:", completion.choices[0].message.parsed)# Validate the response against the original schema if you want to remove the reasoning fieldsfrom uiform._utils.json_schema import filter_auxiliary_fields_jsonassert completion.choices[0].message.content is not Noneextraction = schema_obj.pydantic_model.model_validate( filter_auxiliary_fields_json(completion.choices[0].message.content, schema_obj.pydantic_model))
Next, set up a FastAPI route that will handle incoming webhook POST requests. Below is an example of a simple FastAPI application with a webhook endpoint:
Copy
from fastapi import FastAPI, Requestfrom fastapi.responses import JSONResponsefrom uiform.types.automations.webhooks import WebhookRequestfrom pydantic import BaseModel, Field, ConfigDictapp = FastAPI()@app.post("/webhook")async def webhook(request: WebhookRequest): invoice_object = json.loads(request.completion.choices[0].message.content or "{}") # The parsed object is the same Invoice object as the one you defined in the Pydantic model print("📬 Webhook received:", invoice_object) return {"status": "success", "data": invoice_object}# To run the FastAPI app locally, use the command:# uvicorn your_module_name:app --reloadif __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8000)
To continue, you need to deploy your FastAPI app to a server to make your webhook endpoint publicly accessible. We recommend using Replit to get started quickly if you don’t have a server yet. An alternative is to use ngrok to expose your local server to the internet.
Instead of creating automations directly, you now first create a processor (which contains the extraction configuration) and then attach an automation (which handles triggers like emails) to it.
Copy
from uiform import UiForm# Initialize the UiForm clientuiclient = UiForm()# Step 1: Create a processor with your extraction configurationprocessor = uiclient.processors.create( name="Invoice Processor", model="gpt-4o-mini", modality="native", json_schema={ "X-SystemPrompt": "You are an expert at analyzing invoice documents.", "properties": { "total_amount": { "type": "number", "description": "The total invoice amount", }, "date": { "type": "string", "description": "Invoice date in YYYY-MM-DD format", "X-ReasoningPrompt": "Look for dates labeled as 'Invoice Date', 'Date', etc." } }, "required": ["total_amount", "date"] })# Step 2: Create a mailbox automation and attach it to the processormailbox = uiclient.processors.automations.mailboxes.create( name="Invoice Mailbox", email="invoices@mailbox.uiform.com", processor_id=processor.id, webhook_url="https://your-server.com/webhook", # Replace with your actual webhook URL)print(f"✅ Processor created: {processor.id}")print(f"✅ Mailbox created: {mailbox.email}")
At any email sent to invoices@mailbox.uiform.com, the automation will use your processor configuration to extract data and send a POST request to your FastAPI webhook endpoint.
You can see the processor and automation you just created on your dashboard!
Finally, you can test the processor and automation rapidly with the test functions of the SDK:
Copy
from uiform import UiForm# Initialize the UiForm clientuiclient = UiForm()# If you just want to send a test request to your webhooklog = uiclient.processors.automations.mailboxes.tests.webhook( email="invoices@mailbox.uiform.com", )# If you want to test the file processing logic: log = uiclient.processors.automations.mailboxes.tests.process( email="invoices@mailbox.uiform.com", document="your_invoice_email.eml")# If you want to test a full email forwardinglog = uiclient.processors.automations.mailboxes.tests.forward( email="invoices@mailbox.uiform.com", document="your_invoice_email.eml")
You can also test your webhook locally by overriding the webhook url set in the automation
You can also test your processor and automation directly from the dashboard:
Copy
from uiform import UiFormuiclient = UiForm()# If you just want to send a test request to your webhooklog = uiclient.processors.automations.mailboxes.tests.webhook( email="invoices@mailbox.uiform.com", webhook_url="http://localhost:8000/webhook" # If you want to try your webhook locally, you can override the webhook url set in the automation)
That’s it! You can start processing documents at scale.
You have 1000 free requests to get started, and you can subscribe to the pro plan to get more.
But this minimalistic example is just the beginning. Continue reading to learn more about how to use UiForm to its full potential.