What is snill.ai?

Custom business software from a conversation.

Snill lets you describe what your business needs in plain language — and generates a complete internal system in seconds. You get structured data, dashboards, forms, a REST API, and an AI assistant that can query and update your data.

It's built for founders, consultants, and operators who need custom software but don't want to build it from scratch or duct-tape together a stack of SaaS tools.

What's included

Every Snill app comes with the following out of the box — no add-ons, no integrations to wire up:

How it works

  1. Describe your business — tell the AI what you need. "I run a consulting firm and need to track clients, projects, time entries, and invoices."
  2. Review the result — Snill generates a complete data model with collections, fields, relationships, dashboards, and navigation.
  3. Start using it — your app is live immediately. Add records, view dashboards, invite team members.
  4. Refine as you go — ask the AI to make changes ("add a status field to projects") or use the visual editor to tweak fields, layouts, and settings.
Tip

The AI and the visual editor produce the same output. You can switch between them freely — edits from either are fully compatible.

Creating your first app

From idea to working app in under a minute.

When you sign in to snill.ai, you'll see a text field where you describe what you need. Be as specific or as vague as you like — the AI handles the details.

What to write

Describe your business domain and what you want to track. Good prompts include the types of things you manage and how they relate to each other:

What you get

The AI generates a complete app with:

Everything is editable. You can rename fields, add new collections, change the dashboard — either through the AI chat or the visual Appmodel Editor.

Collections

The building blocks of your app — each one holds a type of data.

A collection is like a database table or a spreadsheet tab. It holds records of the same type — for example, a "Customers" collection holds customer records, and an "Invoices" collection holds invoices.

Each collection has:

You control which fields appear as columns in the list view, which fields are searchable, and how records are sorted by default. Each collection also has a singular label (e.g. "Customer" for the "Customers" collection) used in buttons and form titles like "Add Customer".

Tip

Collection names are used in URLs and the API, so the AI picks short, descriptive names like customers, invoices, or time_entries.

Fields

Define what each record contains — from text and numbers to dates and files.

Every collection has a set of fields that define the shape of its records. Each field has a type, a display label, and optional formatting.

Field types

TypeDescriptionExample
TextShort or long text, email, URLName, description, website
NumberDecimal or whole number, with optional currency or percent formatPrice, quantity, tax rate
BooleanTrue/false checkboxActive, paid, approved
Date / TimeDate, date-time, or time-only (HH:mm)Due date, created at, shift start
DropdownPick from a fixed set of optionsStatus: draft, active, archived
Image / FileUpload an image or documentPhoto, contract PDF
LookupA reference to a record in another collectionCustomer on an invoice

Special field behaviors

Relationships

Connect your collections so data flows between them naturally.

Most business data is connected — invoices belong to customers, tasks belong to projects, employees work in departments. Snill supports several relationship patterns:

Single lookup

The most common type. A field in one collection points to a record in another. For example, an invoice has a customer lookup field that links to the Customers collection. In the form, this renders as a searchable dropdown.

Multi-reference

When a record relates to multiple records in another collection. For example, a project might have several team members, each linking to the Employees collection.

Related collections

The reverse view — when viewing a customer, you can see all their invoices listed below the form. This is configured on the parent collection and shows child records that reference it. You can control which fields display, how they're sorted, and whether users can create new child records directly.

Self-referencing (tree view)

A collection can reference itself to create hierarchies. For example, a "Categories" collection where each category has a parent field pointing to another category. Snill renders this as an expandable tree instead of a flat list.

Linked fields

When you create a lookup, you can also pull in field values from the referenced record. For example, when selecting a product on an order line, automatically copy the product's price into a unit price field. This can either copy once (at creation) or stay in sync when the source changes.

Calculated fields

Automatically compute values from other fields using formulas.

A calculated field updates its value whenever the fields it depends on change. You define a formula, and Snill handles the rest.

Simple formulas

Reference other fields in the same record with arithmetic:

quantity * unitPrice
subtotal * $vatRate

The $ prefix references global app variables — constants like tax rates or hourly rates that you define once and use across formulas.

Aggregations

Calculated fields can also summarize data from related collections. If you have an Invoice with many Line Items, you can calculate totals on the invoice:

FunctionWhat it doesExample
SUMTotal of all valuesSUM(lineItems.amount)
AVGAverageAVG(reviews.rating)
COUNTNumber of recordsCOUNT(tasks)
MINSmallest valueMIN(bids.amount)
MAXLargest valueMAX(offers.price)

Aggregations update automatically when child records are created, edited, or deleted.

Tip

You can combine aggregations with arithmetic. For example, compute a grand total: SUM(lineItems.amount) * $vatRate

Templates

Build display strings from record values using simple interpolation.

A template field assembles text from other fields automatically. Use the $(fieldName) syntax to insert values:

Dear $(contactName), your order $(orderNumber) is ready.

This is useful for creating readable display names, reference strings, or formatted summaries. Templates support dot notation for lookup fields — for example, $(customer.name) pulls the name from a linked customer record.

You can also reference app variables with $variableName, so a template like $(name) — $companyName would include your company name.

Form rules

Make forms dynamic — show, hide, or change fields based on conditions.

Form rules let you control field behavior based on the values of other fields. Each rule has a condition and one or more actions:

Rules evaluate instantly as the user types or selects values — no save required. Each rule can also have an else block for the opposite case.

Tip

Rules are evaluated in order. If multiple rules affect the same field, later rules take precedence.

Form layouts

Organize fields into sections and columns for a clean editing experience.

By default, fields stack in a single column. With form layouts, you can:

For example, a contact form might have a "Contact Info" section with name, email, and phone in 3 columns, and an "Address" section below with street, city, zip, and country in 2 columns.

Fields not assigned to any group render at the bottom in the default layout.

Component groups

Form groups can also contain page-style components (charts, stats, tables) alongside regular fields. This lets you embed live data visualizations directly within a record's form — for example, showing a chart of related invoices right next to the customer's contact details.

Pages

Build custom views with filters, charts, tables, and text — beyond the standard collection views.

Pages are custom views that combine multiple components into a single screen. While collections give you list and detail views, pages let you create dashboards, reports, and overviews that pull data from multiple collections.

Components

A page is a grid of components. Each component has a type and a width (1-4 columns on the grid):

Parameters

Pages can have interactive filters at the top. When users change a filter, all components on the page update to reflect the selection. Parameter types include:

Parameters can have smart defaults like "today's date" or "start of this month" — so the page shows relevant data immediately.

Date filters

Components can use dynamic date values in their queries — like "today", "start of this month", or "7 days ago". These update automatically, so a page showing "this month's sales" always shows the current month.

Tip

Pages appear in the sidebar navigation just like collections. You can even set a page as the app's start page.

Dashboards

A summary view of your most important data — stats, charts, and tables at a glance.

Every app has a dashboard that provides an overview of your data. It's built from widgets arranged on a 4-column grid:

Widget types

WidgetWhat it shows
CountTotal number of records in a collection
StatAn aggregate value — sum, average, min, or max of a field
ChartData grouped and visualized — supports bar, line, area, pie, donut, radar, and more
TableA mini table showing recent or filtered records
ActivityA feed of recent changes across the app

Charts can group data by any field — including dates with configurable granularity (day, week, month, year). Each widget can span 1 to 4 columns for flexible layouts.

The AI generates a sensible dashboard automatically, but you can customize it through the visual editor or by asking the AI to change it.

Access control

Control who can see, edit, and delete data — at the collection, record, and field level.

Collection-level access

Each collection can restrict access by role. There are three modes:

Record ownership

For collections where users should only see or edit their own records, you can set an ownership mode:

ModeSeeEdit / Delete
AllAll recordsAll records
Own-modifyAll recordsOnly their own
OwnOnly their ownOnly their own

Ownership is tracked automatically — the system records who created each record. Admins always have full access regardless of these settings.

Field-level access

Individual fields can be restricted too. For example, a "salary" field might be visible only to the "admin" and "hr" roles, and editable only by "admin".

Members & Billing

Snill is structured around organizations — your team's container for projects, members, and billing.

An organization is the top-level workspace. It contains one or more projects (each project is one app) and a list of members. Members have roles at the organization level:

Inside each project you can also define your own custom roles (e.g. "manager", "viewer", "sales") used by access control rules — see Access control for collection-level and field-level permissions.

Billing & usage

Billing is at the organization level. Free plans include a small number of projects and a generous usage cap to try things out; paid plans unlock unlimited projects, higher record limits, and higher AI quotas, billed per active member. Manage subscription, payment method, and invoices under Settings → Billing (admins only).

Usage is tracked across the whole organization — record count, storage, and AI requests. All members can review usage under Settings → Usage.

Activity Log

A complete audit trail for everything that happens in your app.

The activity log records every data mutation (create, update, delete) along with custom events fired by triggers and action buttons. Each entry captures the user, action, collection, affected record, a short summary, and a timestamp.

Open it from the Activity Log item in the left sidebar. The view is searchable and paginated, with filters by action type and collection.

The activity log is also part of the public REST API. Your app's OpenAPI spec includes a GET /v1/system/activitylog endpoint — useful for exporting audit history or building integrations that watch for specific events. See the API & Integrations section.

Data Assistant

An AI chat that can query your data, generate reports, and modify your app.

The Data Assistant is a built-in AI that understands your app's data model. You can ask it questions in plain language:

Beyond querying, the assistant can also modify your app's structure — adding fields, creating new collections, adjusting layouts, and updating dashboard widgets. It's the same AI that built your app initially, so it understands the full context.

Note

Structural changes (adding fields, modifying the data model) are only available to admin users. Non-admin users can query and explore data but cannot change the app's configuration.

Appmodel Editor

A visual and code-level editor for your app's entire configuration.

The Appmodel Editor gives admins full control over the app's data model — the JSON configuration that defines every collection, field, relationship, layout, and dashboard.

Visual editor

The visual editor lets you configure everything without touching JSON:

JSON schema editor

For advanced users, the JSON tab provides a full code editor with:

Note

The Appmodel Editor is only available to admin users. Changes are validated before saving and create a new version — you can always roll back.

Import / Export

Bring your data in from CSV or JSON, and export it whenever you need.

Importing data

You can import records into any collection from CSV or JSON files. The import wizard guides you through three steps:

  1. Upload — select your CSV or JSON file
  2. Map fields — match columns from your file to fields in the collection. The wizard handles type coercion (e.g. converting text to numbers or dates) and can extend dropdown options if your data contains new values.
  3. Review and import — preview the data before importing. Large imports are processed in the background.

After importing, newly added records are highlighted in the list view so you can verify the results.

Exporting data

Export records from any collection as CSV or JSON. The export includes the currently filtered/searched data, so you can narrow down what you export.

Themes

Change the look and feel of your app with a single setting.

Snill comes with several built-in visual themes that change the colors and styling of your app. You can select a theme in the app settings or ask the AI to apply one.

The default theme works well for most use cases. Other options include a clean neutral grayscale theme and several color themes. Themes let you match your app to your brand or personal preference.

API & Integrations

Every Snill app comes with a complete REST API — auto-generated from your data model, with scoped API keys and OpenAPI/Swagger documentation built in.

Base URL and live spec

The API is served from a single host, regardless of which app it serves — the API key tells Snill which app to operate on. Each collection in your data model becomes a CRUD endpoint:

https://api.snill.ai/v1/data/{collection}

An interactive Swagger UI — with all your collections, fields, and types — is available at https://api.snill.ai/v1/docs/ui. The raw OpenAPI 3.0 spec is at https://api.snill.ai/v1/docs and is auto-generated from your data model. The spec is scoped to the API key you use, so a key with limited scopes only sees the collections and operations it can call.

An excerpt from a typical app's spec looks like this:

{
  "openapi": "3.0.0",
  "info": { "title": "Customers app", "version": "1.0.0" },
  "servers": [{ "url": "/v1" }],
  "paths": {
    "/data/customers": {
      "get": {
        "summary": "List customers",
        "parameters": [
          { "name": "q",      "in": "query", "schema": { "type": "string" } },
          { "name": "sort",   "in": "query", "schema": { "type": "string" } },
          { "name": "limit",  "in": "query", "schema": { "type": "integer" } },
          { "name": "offset", "in": "query", "schema": { "type": "integer" } }
        ],
        "responses": { "200": { "description": "OK" } }
      },
      "post": { "summary": "Create a customer", ... }
    },
    "/data/customers/{id}": {
      "get":    { "summary": "Get a customer" },
      "patch":  { "summary": "Update a customer" },
      "delete": { "summary": "Delete a customer" }
    }
  }
}

Note that paths are relative to servers[0].url — so the full URL for the list endpoint above is https://api.snill.ai/v1/data/customers. Most OpenAPI tools (and AI assistants) handle the join automatically.

Using the spec with AI assistants

Because the OpenAPI spec is auto-generated and complete, you can hand it directly to an AI coding assistant — Claude, Codex, Cursor, ChatGPT, and others — and have it write integrations or query your data without reading the API surface yourself.

Fetch your spec once with your API key:

curl -H "X-API-Key: snill_01ARZ3NDEKTSV4RRFFQ69G5FAV_..." https://api.snill.ai/v1/docs > snill-spec.json

Then paste the spec into a prompt and describe what you want. For example:

The assistant uses the spec to pick the right endpoints, headers, and query syntax — no manual API reading required.

Authentication

External callers authenticate with an API key. You manage keys under Manage → API Keys in your app. Pass the key in either header on every request:

X-API-Key: snill_01ARZ3NDEKTSV4RRFFQ69G5FAV_a1b2c3d4e5f6789012345678901234ab
Authorization: Bearer snill_01ARZ3NDEKTSV4RRFFQ69G5FAV_a1b2c3d4e5f6789012345678901234ab

Keys can be granted one of two scope shapes:

Keys can be deactivated at any time or set to expire on a specific date. All requests are rate-limited to 60 requests per minute per key (current usage is returned in the X-RateLimit-Limit and X-RateLimit-Remaining response headers).

CRUD endpoints

The examples below use a customers collection. Substitute your own collection name in the URL.

List records

GET /v1/data/customers
  ?q={"status":"active"}    # filter (JSON, URL-encoded)
  &sort=-createdAt          # sort (- prefix means descending)
  &limit=20&offset=0        # pagination (default 20, max 1000)

Returns a JSON array of records. The response also includes X-Total-Count and X-Has-More headers so you can paginate without re-counting.

Get one record

GET /v1/data/customers/{id}

Create a record

POST /v1/data/customers
Content-Type: application/json

{ "name": "Acme", "email": "contact@acme.com" }

Returns 201 Created with the full record — including auto-generated fields, calculated fields, and resolved lookups.

Update a record

PATCH /v1/data/customers/{id}
Content-Type: application/json

{ "name": "Acme Inc" }

Partial — only fields in the body are updated. Returns the merged record. (Snill does not support PUT; PATCH is the only update verb.)

Delete a record

DELETE /v1/data/customers/{id}

Returns 204 No Content.

Querying and filtering

Pass filter conditions as a JSON object in the q parameter (URL-encoded). Snill supports operators familiar from MongoDB:

Sort with ?sort=name (ascending) or ?sort=-createdAt (descending). Combine multiple fields with commas: ?sort=status,-createdAt.

Paginate with ?limit=20&offset=40. The maximum limit is 1000; the default is 20.

Pagination is mandatory — there's no "fetch all" endpoint. For large collections, iterate with offset to walk through results in batches. The X-Total-Count header on every response tells you when you're done.

Errors

Errors return a JSON body with a human-readable message and a stable code:

{
  "error": "API key does not have read access to this collection",
  "code": "SCOPE_DENIED"
}

Common codes you'll see:

CodeStatusMeaning
MISSING_API_KEY401No X-API-Key or Authorization header on the request.
INVALID_API_KEY401The key isn't recognized.
KEY_INACTIVE403The key has been deactivated.
KEY_EXPIRED403The key's expiresAt date has passed.
SCOPE_DENIED403The key doesn't have the required operation on this collection.
RATE_LIMIT_EXCEEDED429You've exceeded 60 requests per minute on this key.
Outbound webhooks

Snill can also push notifications outward — POSTing record changes to URLs you configure, with HMAC-signed payloads. See Webhooks for details.

Webhooks

Notify external systems when data in your app changes.

Outbound webhooks let you POST a payload to any HTTPS URL whenever a record is created, updated, or deleted — and when custom events fire from triggers or action buttons. You configure them under Manage → Webhooks: pick the collection, the events to listen for, and the URL to call. Up to 10 webhooks per project.

Each delivery is a JSON POST containing the event name, collection name, the current record (and the previous record for updates), and a project identifier. The request is signed with an X-Snill-Signature: sha256=... header — an HMAC-SHA256 of the raw request body using the webhook's secret. Verify the signature on your end before trusting the payload.

Deliveries are queued and retried on non-2xx responses. Delivery history (status, response code, latency) is visible in the Webhooks tab, where you can also send test events while developing your integration.

Verifying signatures

Every delivery includes an X-Snill-Signature header. Verify it by computing HMAC-SHA256 of the raw request body using your webhook's secret, and comparing it to the header value:

// Node.js
const crypto = require('crypto');

function verifyWebhook(rawBody, signature, secret) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}
# Python
import hmac, hashlib

def verify_webhook(raw_body, signature, secret):
    expected = 'sha256=' + hmac.new(
        secret.encode(), raw_body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected)

Always verify against the raw request body, not a re-serialized object — even a whitespace difference will change the hash. Use constant-time comparison (timingSafeEqual / compare_digest) to prevent timing attacks.

Triggers

Fire custom events on data changes — declaratively, no code required.

Triggers are rules attached to a collection that emit named events when conditions are met. Two kinds:

You define triggers visually in the Appmodel Editor when editing a collection — no code needed. The events they emit are picked up by webhooks (so external systems can react) and recorded in the activity log (for debugging and audit).