1

Setup the Python SDK

Install the UiForm Python SDK and configure your API keys to start processing documents with your preferred AI provider

2

Create your JSON schema

Define the structure of the data you want to extract from your documents using our schema format with custom prompting capabilities

3

Create your FastAPI server with a webhook

Set up an endpoint that will receive the structured data extracted from your documents after processing

4

Create your processor and automation

Configure a processor and an automation to handle triggers like emails and send results to your webhook

5

Test your processor and automation

Validate your setup by sending test documents through your processor and verify the extracted data matches your requirements

Step 1: Setup the Python SDK

To get started, install the uiform package using pip:

pip install uiform

Then, create your API key on uiform.com and populate your env variables:

.env
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:

import uiform
import os

uiclient = uiform.UiForm()

uiclient.secrets.external_api_keys.create(
    provider="OpenAI", 
    api_key=os.getenv("OPENAI_API_KEY")
)

Process your first document

Here is how to process your first document with the create_messages method:

from uiform import UiForm
from openai import OpenAI

# Initialize UiForm client
uiclient = UiForm()

# Convert any document into LLM-ready format
doc_msg = uiclient.documents.create_messages(
    document = "invoice.pdf"  # Works with PDFs, Excel, emails, etc.
)

client = OpenAI()
completion = client.chat.completions.create(
    model="gpt-4.1-nano",
    messages=doc_msg.openai_messages + [
        {
            "role": "user",
            "content": "Summarize the document"
        }
    ]
)

Step 2 : Create your JSON Schema

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.

from uiform import UiForm
from openai import OpenAI
from pydantic import BaseModel, Field, ConfigDict

# Define your extraction schema
class 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 data
uiclient = UiForm()
doc_msg = uiclient.documents.create_messages(
    document = "invoice.pdf"
)
schema_obj = uiclient.schemas.load(
    pydantic_model = Invoice
)

# Extract structured data with any LLM
client = 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 fields
from uiform._utils.json_schema import filter_auxiliary_fields_json
assert completion.choices[0].message.content is not None
extraction = schema_obj.pydantic_model.model_validate(
    filter_auxiliary_fields_json(completion.choices[0].message.content, schema_obj.pydantic_model)
)

Step 3: Create your FastAPI server with a webhook

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:

from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from uiform.types.automations.webhooks import WebhookRequest
from pydantic import BaseModel, Field, ConfigDict

app = 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 --reload
if __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.

Step 3.1: Exposing local server to the internet using ngrok

We have a very simple Dockerfile that fastapi+ngrok to get you started. Check out the webhook_server folder for more details.

You will need a ngrok auth token to run the docker container. You can get one here

Start fastapi+ngrok server:

git clone https://github.com/UiForm/uiform.git
cd uiform/examples/webhook_server
docker build -t webhook_server .
docker run --rm -it -e NGROK_AUTH_TOKEN=[your_ngrok_auth_token] webhook_server

Take note of the webhook URL, you will need it on the next steps.

Step 4: Create your processor and automation

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.

from uiform import UiForm

# Initialize the UiForm client
uiclient = UiForm()

# Step 1: Create a processor with your extraction configuration
processor = 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 processor
mailbox = 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!

Step 5: Test your processor and automation

Finally, you can test the processor and automation rapidly with the test functions of the SDK:

from uiform import UiForm

# Initialize the UiForm client
uiclient = UiForm()

# If you just want to send a test request to your webhook
log = 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 forwarding
log = 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:

from uiform import UiForm

uiclient = UiForm()

# If you just want to send a test request to your webhook
log = 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.