---
name: forminit-python
description: Integrates Forminit headless form backend into Python applications using the Python SDK. Submits forms with typed blocks (sender, text, email, phone, file, rating, date, select, radio, checkbox, country, number). Works with Flask, Django, FastAPI, and plain Python. Use when handling form submissions from any Python backend. Do not use for JavaScript/TypeScript projects (use forminit-nodejs, forminit-nextjs, or forminit-html).
metadata:
  author: forminit
  version: "1.0"
---

# Forminit — Python Integration

> Forminit is a headless form backend API. You build the form UI, Forminit handles submission, validation, storage, and notifications. This skill uses the Python SDK.

## Constraints

- Server-side only. API key must be in environment variables, never in client code.
- User must set authentication mode to "Protected" in Form settings before creating an API token.
- All field names use prefix pattern: `fi-{type}-{name}`
- Sender block: max 1 per form. Use for submitter's contact info.
- Default to `fi-sender-firstName` + `fi-sender-lastName`. Use `fi-sender-fullName` only if user wants a single name field.
- For non-submitter emails/phones/countries, use field blocks: `fi-email-invitee`, `fi-phone-emergency`, `fi-country-shipping`
- File uploads use flat form data with tuples, not JSON
- Max 50 blocks per form, max 25 MB total files
- Python 3.8+ required

## Install

```bash
pip install forminit==0.1.0
```

## Environment Variable

```bash
# .env
FORMINIT_API_KEY="YOUR-SECRET-API-TOKEN"
```

## Basic Submission (Structured Blocks)

```python
import os
from forminit import ForminitClient

forminit = ForminitClient(api_key=os.environ["FORMINIT_API_KEY"])

response = forminit.submit("FORM_ID", {
    "blocks": [
        {
            "type": "sender",
            "properties": {
                "firstName": "Jane",
                "lastName": "Doe",
                "email": "jane@example.com",
            },
        },
        {"type": "text", "name": "message", "value": "Hello from Python"},
    ]
})

print(response["hashId"])
forminit.close()
```

Replace `FORM_ID` with the form ID from https://app.forminit.com.

## Context Manager

```python
with ForminitClient(api_key=os.environ["FORMINIT_API_KEY"]) as client:
    response = client.submit("FORM_ID", {
        "blocks": [
            {"type": "sender", "properties": {"email": "jane@example.com"}},
            {"type": "text", "name": "message", "value": "Hello"},
        ]
    })
```

## Flat Form Data Submission

```python
response = client.submit("FORM_ID", {
    "fi-sender-email": "jane@example.com",
    "fi-sender-firstName": "Jane",
    "fi-text-message": "Hello from Python",
})
```

## File Upload

```python
response = client.submit("FORM_ID", {
    "fi-sender-email": "jane@example.com",
    "fi-text-message": "See attached",
    "fi-file-resume": ("resume.pdf", open("resume.pdf", "rb"), "application/pdf"),
})
```

## Flask Example

```python
from flask import Flask, request, jsonify
from forminit import ForminitClient
import os

app = Flask(__name__)
client = ForminitClient(api_key=os.environ["FORMINIT_API_KEY"])

@app.route("/submit", methods=["POST"])
def submit():
    data = request.get_json()
    response = client.submit("FORM_ID", {"blocks": data["blocks"]})
    return jsonify({"id": response["hashId"]})
```

Full Flask guide: https://forminit.com/docs/flask/

## Django Example

```python
# views.py
import json, os
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from forminit import ForminitClient

client = ForminitClient(api_key=os.environ["FORMINIT_API_KEY"])

@csrf_exempt
def submit(request):
    if request.method != "POST":
        return JsonResponse({"error": "Method not allowed"}, status=405)
    data = json.loads(request.body)
    response = client.submit("FORM_ID", {"blocks": data["blocks"]})
    return JsonResponse({"id": response["hashId"]})
```

Full Django guide: https://forminit.com/docs/django/

## FastAPI Example

```python
from fastapi import FastAPI
from forminit import ForminitClient
import os

app = FastAPI()
client = ForminitClient(api_key=os.environ["FORMINIT_API_KEY"])

@app.post("/submit")
async def submit(payload: dict):
    response = client.submit("FORM_ID", {"blocks": payload["blocks"]})
    return {"id": response["hashId"]}
```

Full FastAPI guide: https://forminit.com/docs/fastapi/

## Form Blocks Reference

### Sender Block — `fi-sender-{property}`

One per form. Collects submitter info. At least one property required.

```
fi-sender-email        fi-sender-firstName    fi-sender-lastName     fi-sender-fullName
fi-sender-phone        fi-sender-company      fi-sender-position     fi-sender-title
fi-sender-userId       fi-sender-address      fi-sender-city         fi-sender-country
```

### Field Blocks — `fi-{type}-{name}`

Each name must be unique. Up to 50 total blocks.

```
text      fi-text-{name}       e.g. fi-text-message
number    fi-number-{name}     e.g. fi-number-quantity
email     fi-email-{name}      e.g. fi-email-invitee
phone     fi-phone-{name}      e.g. fi-phone-emergency          Validation: E.164 (+12025550123)
url       fi-url-{name}        e.g. fi-url-website
date      fi-date-{name}       e.g. fi-date-appointment         Validation: ISO 8601 (YYYY-MM-DD)
rating    fi-rating-{name}     e.g. fi-rating-satisfaction      Validation: integer 1-5
select    fi-select-{name}     e.g. fi-select-plan              Supports multi-select (list)
radio     fi-radio-{name}      e.g. fi-radio-priority           Single choice (str)
checkbox  fi-checkbox-{name}   e.g. fi-checkbox-features        Supports multi-choice (list)
file      fi-file-{name}       e.g. fi-file-resume              Tuple: (filename, file_obj, mime_type)
country   fi-country-{name}    e.g. fi-country-shipping         Validation: ISO 3166-1 alpha-2
```

## Common Mistakes

- Using `name="email"` instead of `fi-sender-email` — all fields must use the `fi-` prefix or the submission will be empty.
- Using `fi-sender-name` — there is no `name` property on sender. Use `fi-sender-firstName` + `fi-sender-lastName` or `fi-sender-fullName`.
- Putting the API key in client-side code — the key must stay on the server.
- Setting form authentication to "Public" instead of "Protected" — Python integration requires Protected mode.
- Sending phone without E.164 format — `5551234567` will be rejected, must be `+15551234567`.
- Using structured blocks JSON with file uploads — files require flat form data with tuples.
- Forgetting to call `client.close()` — use the context manager (`with`) to handle cleanup automatically.

## Documentation

- Python guide: https://forminit.com/docs/python/
- Python SDK reference: https://forminit.com/docs/python-sdk/
- Flask guide: https://forminit.com/docs/flask/
- Django guide: https://forminit.com/docs/django/
- FastAPI guide: https://forminit.com/docs/fastapi/
- Form blocks: https://forminit.com/docs/form-blocks/

## Spam Protection (only if user requests)

- reCAPTCHA: https://forminit.com/docs/recaptcha/
- hCaptcha: https://forminit.com/docs/hcaptcha/
