Files
OSIT-AE-API-FastAPI/documentation/V3_FRONTEND_API_GUIDE.md

9.8 KiB

Aether API V3 Frontend Integration Guide (Svelte/TypeScript)

This guide explains how to update or create frontend functions to interact with the new Aether API V3 CRUD endpoints. V3 introduces a nested URL structure, a powerful POST-based search, and improved performance.


1. Key Differences (V2 vs V3)

Feature CRUD V2 (Legacy) CRUD V3 (Modern)
Base Prefix /v2/crud /v3/crud
List Suffix Uses /list No suffix (e.g., /v3/crud/journal/)
Nested Path Not supported in URL Supported (e.g., /v3/crud/journal/{id}/journal_entry/)
View Selection tbl_alt, mdl_alt view parameter (e.g., ?view=enriched)
Complex Search Limited to GET jp POST /search (Unlimited size + Hybrid params)
Full-Text Search Manual column names Reserved q property in SearchQuery

2. Authentication and Security (Mandatory in V3)

As of January 2026, the V3 architecture enforces strict Multi-Tenant Isolation and Machine Authorization. Most requests now require two levels of validation.

A. The "Entry Ticket" (API Key)

Mandatory for all requests. To prevent unauthorized clients from using the API, every request must provide a valid x-aether-api-key in the header. This identifies the application or script (e.g., the Svelte frontend, the Flask legacy app, or an AI agent).

  • Header: x-aether-api-key: <your_app_key>
  • Status Code: 403 Forbidden if missing, invalid, or expired.

B. The "Visa" (Account Context)

Once the API Key is validated, you must specify the context of your request.

  1. Standard User Access: Provide the x-account-id (the random string ID). This restricts all results to that specific tenant.
    • Header: x-account-id: <account_id_random>
  2. Administrative Bypass: For authorized scripts needing global access, use the bypass header.
    • Header: x-no-account-id: bypass
  3. Authentication Requirement: Most V3 CRUD endpoints also require a standard Bearer token in the Authorization header for user-level actions.

C. The "Bootstrap Paradox" Exception (site_domain)

There is one critical exception to strict authentication: site_domain search.

Because the frontend needs to lookup the site configuration (to know which account it's on) before it has an Account ID or User token, the following endpoint allows unauthenticated (guest) access:

Endpoint: POST /v3/crud/site_domain/search

This is the only V3 search allowed without an Account ID or JWT. However, it still requires a valid API Key.

D. Fail-Fast on Auth Failures (401/403)

To prevent unnecessary server load and looping, the frontend implements a Fail-Fast policy for authentication and authorization errors.

  • 401 Unauthorized: Means the token is missing, invalid, or expired.
  • 403 Forbidden: Means the token is valid, but you do not have permission to access the specific resource (or you attempted a cross-tenant request without bypass).
  • Protocol: If the API returns a 401 or 403, the frontend must not retry the request automatically. It should stop immediately and, if applicable, redirect the user to the login page or display a "Permission Denied" message.

3. Implementing V3 CRUD Functions

A. List & Single Object (GET)

Support for view selection allows fetching richer data models when needed.

// Example: Get enriched journal details
// GET /v3/crud/journal/{id}?view=enriched
export async function get_ae_obj_v3({ api_cfg, obj_type, obj_id, view = 'default' }) {
    const endpoint = `/v3/crud/${obj_type}/${obj_id}`;
    return await get_object({ api_cfg, endpoint, params: { view } });
}

B. Advanced & Hybrid Search (POST)

The /search endpoint combines the power of complex logical bodies with the simplicity of query parameters.

export async function search_ae_obj_v3({
    api_cfg,
    obj_type,
    search_query, // { q: "search term", and: [...] }
    enabled = 'enabled',
    view = 'default',
    for_obj_type,
    for_obj_id
}) {
    const endpoint = `/v3/crud/${obj_type}/search`;
    
    // Standard filters can be passed as query params
    const params: any = { enabled, view };
    if (for_obj_type) params.for_obj_type = for_obj_type;
    if (for_obj_id) params.for_obj_id = for_obj_id;

    return await post_object({ 
        api_cfg, 
        endpoint, 
        params, 
        data: search_query 
    });
}

C. Standardized Global Search (q)

Use the q property in your search body for a keyword search.

  • Wildcard Support: Setting q: "%" will bypass all text filtering and return all records (respecting only logical filters like enable).
  • Fallback: If the table lacks a default_qry_str column, the API automatically performs a LIKE search across all searchable fields.
{
  "q": "Annual Meeting",
  "and": [{ "field": "enable", "op": "eq", "value": true }]
}

4. Create, Update, & Delete (POST, PATCH, DELETE)

V3 supports both top-level operations and nested parent/child operations.

A. Create (POST)

When creating objects, V3 strictly validates the incoming JSON against the mdl_in Pydantic model.

// POST /v3/crud/{obj_type}/
export async function create_ae_obj_v3({ api_cfg, obj_type, data }) {
    const endpoint = `/v3/crud/${obj_type}/`;
    return await post_object({ api_cfg, endpoint, data });
}

// POST /v3/crud/{parent_obj_type}/{parent_obj_id}/{child_obj_type}/
// Note: Parent ID is automatically injected into the child record.
export async function create_nested_obj_v3({ api_cfg, parent_type, parent_id, child_type, data }) {
    const endpoint = `/v3/crud/${parent_type}/${parent_id}/${child_type}/`;
    return await post_object({ api_cfg, endpoint, data });
}

B. Update (PATCH)

V3 uses PATCH for partial updates. Only the fields provided in the body will be modified in the database.

// PATCH /v3/crud/{obj_type}/{obj_id}
export async function update_ae_obj_v3({ api_cfg, obj_type, obj_id, data }) {
    const endpoint = `/v3/crud/${obj_type}/${obj_id}`;
    return await patch_object({ api_cfg, endpoint, data });
}

C. Delete (DELETE)

The DELETE method is used for removal. The backend may implement soft-delete (hide/disable) depending on the configuration.

// DELETE /v3/crud/{obj_type}/{obj_id}
export async function delete_ae_obj_v3({ api_cfg, obj_type, obj_id }) {
    const endpoint = `/v3/crud/${obj_type}/${obj_id}`;
    return await delete_object({ api_cfg, endpoint });
}

5. Specialized & Context Endpoints

A. Context Resolution (FQDN)

Used during initial load to find the account_id associated with the domain.

Legacy Method: GET /crud/site/domain/{fqdn}?use_alt_table=true&use_alt_base=true Modern V3 Method (Preferred): POST /v3/crud/site_domain/search with { "q": "your-domain.com" }

B. Schema Discovery

Path: GET /v3/crud/{obj_type}/schema

Used for developer tools or dynamic UI builders to understand the structure of an AE object.

// GET /v3/crud/account/schema
export async function get_obj_schema_v3({ api_cfg, obj_type }) {
    const endpoint = `/v3/crud/${obj_type}/schema`;
    return await get_object({ api_cfg, endpoint });
}

6. Secure File Downloads (URL Parameter)

For hosted_file and event_file, browsers often need to download files without complex header modifications. In these cases, you can pass the JWT directly in the URL.

// Example: Creating a secure download link for a browser
// GET /v3/crud/hosted_file/{id}/?jwt={token}
const downloadUrl = `${BASE_URL}/v3/crud/hosted_file/${fileId}/?jwt=${jwtToken}`;

7. The "ID Vision" Standard (2026 Refactor)

V3 has moved to a String-Only ID Vision. In this paradigm, the frontend (SvelteKit) and external agents NEVER handle or store internal database integers.

A. Automatic ID Mapping (Output)

All V3 endpoints now automatically rename internal id_random fields to clean, short names in the JSON response.

  • id: The unique random identifier for the primary record.

  • account_id: The unique random identifier for the parent account.

  • site_id, person_id, etc: All related IDs follow this clean naming.

Example Response:


{

  "id": "OGQK-02-04-94",

  "name": "Scott's Journal",

  "account_id": "nqOzejLCDXM"

}

B. Intelligent Mapping (Input/Search)

The backend is now "Vision Aware." You can send string IDs back to the API using the same clean names.

  • Search Filters: You can filter by account_id using the random string value. The API handles the mapping to the DB integer automatically.

  • Create/Update: You can send account_id: "nqOzejLCDXM" in a payload. The sanitize_payload logic resolves this to the correct integer before saving.

Preferred Filter Pattern:


{

  "and": [{ "field": "account_id", "op": "eq", "value": "nqOzejLCDXM" }]

}


8. Standard Patterns & Common Pitfalls (2026 Update)

A. Permissive Update Mode (Upcoming)

Status: In Development.

To simplify frontend state management, V3 is introducing a Permissive Update Mode. When enabled, the backend will ignore extra fields in your PATCH payload (like _processed_at or created_on) instead of returning a 400 Bad Request.

  • Header: x-ae-ignore-extra-fields: true

This allows you to send back larger chunks of the object without meticulously whitelisting every field, though whitelisting remains a best practice for performance.

B. Handle 'NULL' vs '0' for Booleans

Pitfall: Fields like hide or priority can be NULL in the database.

Standard: Ensure your synchronization logic in components handles null, undefined, and 0 identically for boolean checks to prevent "ghost" changes being detected by Svelte 5.