Developer resources

Southwind AI Developer Portal

Authenticate, create a Rover report, and handle asynchronous results through the Southwind API.

Authentication

Send your organization API key in the X-API-Key header. Keep it in a secret manager or environment variable; the full secret is shown only once when the key is created. Building a startup? Email startup@southwind.ai to unlock an API key with 100 free Rover reports.

curl https://app.southwind.ai/api/v1/me \
  --header 'X-API-Key: $SOUTHWIND_API_KEY'

Core concepts

Three resources carry data from an upstream connection into an evidence-backed investigation.

Data Origin
The upstream connection or uploaded file. One origin can contain several data sources.
Data Source
A specific queryable dataset derived from an origin. Reports reference data source UUIDs, not origin IDs.
Report
An asynchronous analysis task created from data sources that are ready to use.

Quickstart: origin to report

Create an origin, resolve its source IDs, and wait until every required source is ready before creating a report.

  1. Create an origin with POST /api/v1/origins/file/, POST /api/v1/origins/data-api/, or POST /api/v1/origins/postgres/.
  2. Resolve the data source IDs from the connector-specific receipt.
  3. For asynchronous file and API origins, wait for every required source to become completed. Prefer the origin ingest event stream; source polling is the fallback.
  4. If a source reaches awaiting_confirmation, inspect metadata.cleaning_plan and submit the required decisions.
  5. If a file or API source reaches failed, inspect ingest_error_code and retry ingestion.
  6. Create the report with POST /api/v1/reports/ and the completed source IDs.

A file or API origin creation response is a receipt: tabular sources are queued, not immediately ready. Creating a report from a tabular source that is not completed returns 409 Conflict. PDF, TXT, and Markdown document sources are exempt from this readiness gate.

Connector receipts

The three origin connectors intentionally return different receipt shapes.

ConnectorReceiptHow to resolve sources
Filecreated_data_origins[].idSource discovery is asynchronous. Stream the origin or call GET /api/v1/sources/?origin_id=<origin-id>; do not expect source IDs in the receipt.
Data APIdata_sources[].idPoll each returned source with GET /api/v1/sources/?source_id=<source-id>.
PostgreSQLorigin_idCall GET /api/v1/sources/?origin_id=<origin-id>. Live sources are registered ready for querying.

Ingestion lifecycle

A tabular source moves through a readiness lifecycle before it can be used in a report.

Create origin
     │
     ▼
  queued → ingesting → analyzing
                         │
              ┌──────────┴──────────┐
              ▼                     ▼
 awaiting_confirmation            failed
              │                     │
              ▼                     ▼
          cleaning             ingest/retry
              │                     │
              └──────────┬──────────┘
                         ▼
                     completed
                         │
                         ▼
                   Create report
StatusMeaningUsable in a report?
queuedAccepted and waiting for a worker.No
ingestingReading and materializing upstream data.No
analyzingProfiling types and cleaning requirements.No
awaiting_confirmationAmbiguous conversions require your decision.No
cleaningApplying confirmed conversions.No
completedReady for analysis.Yes
failedIngestion stopped; inspect ingest_error_code.No

Data source responses also expose ingest_run_id, row_count, and size_bytes. Nullable fields such as ingest_error_code are omitted when there is no value. collection_name is deprecated and no longer identifies a queryable location.

Live PostgreSQL sources are queried directly through DuckDB rather than materialized, so size_bytes is 0. Origin-level lifecycle propagation for PostgreSQL is still in progress; the origin status is not yet a stable readiness contract.

Wait with SSE or polling

The preferred origin stream emits per-source status snapshots approximately every two seconds and closes at completed, awaiting_confirmation, or failed.

event: status
data: [{"id":"<source-id>","ingest_status":"analyzing","ingest_error_code":null}]

event: done
data: {}

As a fallback, call GET /api/v1/sources/?origin_id=<origin-id> every 2–5 seconds.

Do not treat a successful source sync, update, or creation response as proof that fresh data is ready. Wait for the new ingestion run to finish.

Create a report

Rover is the recommended starting point for autonomous analysis. Run it across one or more completed data sources and orient the investigation with a goal.

curl --request POST 'https://app.southwind.ai/api/v1/reports/' \
  --header 'X-API-Key: $SOUTHWIND_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{"agent_id":"rover_report","data_sources_ids":["DATA_SOURCE_ID"],"title":"Performance investigation","params":{"language":"english","currency":"USD","data_provenance":true,"target_sections":7,"goal":"Identify the main performance drivers and prioritize actions."}}'

Report status normally progresses from queued to running, then to completed or failed. waiting_for_data is also an in-progress state and commonly appears when redoing a report while its sources refresh. Clients should tolerate new in-progress values.

Inspect the OpenAPI schema

Cleaning confirmation

Every column marked ambiguous in metadata.cleaning_plan needs a decision. Send the offered temporal format, a numeric interpretation, or keep_text.

{
  "decisions": [
    {
      "column": "order_date",
      "kind": "temporal",
      "target_type": "date",
      "format": "%d/%m/%Y"
    }
  ],
  "locale": "eu"
}

The source moves to cleaning, then completed. Decisions are retained for later refreshes when they still match the incoming data.

Report translations

Translate a completed report with POST /api/v1/reports/{task_id}/translate, then poll the report until the language appears in available_languages. While work is pending, translation_jobs exposes the per-language state.

POST /api/v1/reports/{task_id}/translate

GET /api/v1/reports/{task_id}?language=italian

The language query parameter is also available for report save, rename, export, and public report reads. These writes affect only the selected language. If the primary report changes later, translated views remain available with translation_stale: true until replaced.

Submitting an existing or active translation without replace: true returns 409 with translation_exists or translation_in_progress. Unsupported languages return 400 invalid_language; non-completed reports return 409 report_not_ready. Translation usage is metered separately.

Errors

API errors use a stable JSON envelope with a human-readable detail, a machine-readable code, and a request ID.

{"error":{"detail":"...","code":"...","request_id":"..."}}

Branch on the stable machine-readable code, not the human-readable detail. Server-side failures normally include a request ID.

Full reference

Explore every endpoint

Watch the API reference in action, then use Scalar to inspect every request and response schema.

Read the complete API documentation