Quickstart

From API key to evaluation result in six calls — pick a template, create a submission, upload a file, register a webhook, and start the evaluation.

This walks the full happy path: authenticate, pick a template, create a submission, upload a document, register a webhook, and start the evaluation that pushes the result back to you. Every step shows curl and the three SDKs — pick your language with the tabs.

Note

The base URL https://api.aircredit.de/partner is the real one, but it is not publicly reachable yet. Requests in this guide will not reach a live server; they exist to show the shape of each call.

0. Get an API key

Your API key is issued per API client from the AirCredit console. Treat it like a password: it grants full access to your client's submissions. Keep it server-side and pass it as a bearer token on every request. See Authentication for handling and rotation.

Set it once in your environment:

export AIRCREDIT_API_KEY="sk_live_your_key_here"

1. Pick a template

A template defines what a submission collects and verifies: its document slots, checks, and data fields. list_templates returns the builtin templates (stable string keys) and any custom templates you have authored — the default builtin is a fine starting point. See Templates & customization for authoring your own.

curl https://api.aircredit.de/partner/v1/templates \
  -H "Authorization: Bearer $AIRCREDIT_API_KEY"
const templates = await client.listTemplates();

const template = templates.data.find(t => t.isDefault) ?? templates.data[0];
console.log(template.id, template.displayName); // -> "default_personal_real_estate_financing", …
templates = client.list_templates()

template = next((t for t in templates.data if t.is_default), templates.data[0])
print(template.id, template.display_name)  # -> "default_personal_real_estate_financing", …
let templates = client.list_templates().send().await?.into_inner();

let template = templates.data.iter()
    .find(|template| template.is_default)
    .unwrap_or(&templates.data[0]);
println!("{} {}", template.id, template.display_name);

2. Create a submission

A submission is created from the template and keyed by your own external_submission_id. That external id is the idempotency key — reuse it and you get the same submission back instead of a duplicate.

curl -X POST https://api.aircredit.de/partner/v1/submissions \
  -H "Authorization: Bearer $AIRCREDIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "template_id": "6f5f0f0a-1a11-4a7e-9c9e-9a1b0c3d2e4f",
    "external_submission_id": "loan-2026-000451",
    "metadata": { "branch": "muc-01" },
    "language": "de"
  }'
import { createClient } from "@aircredit/partner-sdk";

const client = createClient({ apiKey: process.env.AIRCREDIT_API_KEY! });

const submission = await client.createSubmission({
  templateId: "6f5f0f0a-1a11-4a7e-9c9e-9a1b0c3d2e4f",
  externalSubmissionId: "loan-2026-000451",
  metadata: { branch: "muc-01" },
  language: "de",
});

console.log(submission.id, submission.status); // -> "…", "collecting_documents"
import os
from aircredit_partner import Client

client = Client(api_key=os.environ["AIRCREDIT_API_KEY"])

submission = client.create_submission(
    template_id="6f5f0f0a-1a11-4a7e-9c9e-9a1b0c3d2e4f",
    external_submission_id="loan-2026-000451",
    metadata={"branch": "muc-01"},
    language="de",
)

print(submission.id, submission.status)  # -> "…", "collecting_documents"
use aircredit_partner::Client;

let client = Client::new_with_api_key(
    "https://api.aircredit.de/partner",
    std::env::var("AIRCREDIT_API_KEY")?,
);

let submission = client
    .create_submission()
    .body_map(|b| b
        .template_id("6f5f0f0a-1a11-4a7e-9c9e-9a1b0c3d2e4f")
        .external_submission_id("loan-2026-000451")
        .language(Some("de".to_string())))
    .send()
    .await?
    .into_inner();

println!("{} {:?}", submission.id, submission.status);

A 201 Created means authorization has fully propagated and the submission is immediately usable. If propagation is still catching up you may get 503 authz_not_applied_yet — just retry with the same external_submission_id. Keep the returned id; you need it for everything below.

3. Upload a document

Send the file as multipart/form-data with an external_file_id text part and a file part. The external_file_id is this upload's idempotency key. Accepted types are PDF, JPEG, PNG, and Word; the default size limit is 100 MiB. See Uploads for the full rules.

curl -X POST \
  https://api.aircredit.de/partner/v1/submissions/$SUBMISSION_ID/files \
  -H "Authorization: Bearer $AIRCREDIT_API_KEY" \
  -F "external_file_id=payslip-jan" \
  -F "file=@./payslip-january.pdf;type=application/pdf"
import { readFile } from "node:fs/promises";

const upload = await client.createUpload({
  submissionId: submission.id,
  externalFileId: "payslip-jan",
  file: new File([await readFile("./payslip-january.pdf")], "payslip-january.pdf", {
    type: "application/pdf",
  }),
});

console.log(upload.uploadStatus); // -> "processing"
with open("payslip-january.pdf", "rb") as f:
    upload = client.create_upload(
        submission_id=submission.id,
        external_file_id="payslip-jan",
        file=("payslip-january.pdf", f, "application/pdf"),
    )

print(upload.upload_status)  # -> "processing"
let bytes = tokio::fs::read("payslip-january.pdf").await?;

let upload = client
    .create_upload()
    .submission_id(&submission.id)
    .external_file_id("payslip-jan")
    .file(bytes, "payslip-january.pdf", "application/pdf")
    .send()
    .await?
    .into_inner();

println!("{:?}", upload.upload_status); // -> Processing

You get 202 Accepted with the upload in status processing. Processing is asynchronous — don't block on the response. Until webhooks are wired up (next step), you can poll the submission and watch each upload's upload_status move to ready:

curl https://api.aircredit.de/partner/v1/submissions/$SUBMISSION_ID \
  -H "Authorization: Bearer $AIRCREDIT_API_KEY"
const detail = await client.getSubmission({ submissionId: submission.id });

for (const slot of detail.documentSlots) {
  for (const u of slot.uploads) {
    console.log(slot.key, u.externalFileId, u.uploadStatus);
  }
}
detail = client.get_submission(submission_id=submission.id)

for slot in detail.document_slots:
    for u in slot.uploads:
        print(slot.key, u.external_file_id, u.upload_status)
let detail = client
    .get_submission()
    .submission_id(&submission.id)
    .send()
    .await?
    .into_inner();

for slot in &detail.document_slots {
    for upload in &slot.uploads {
        println!("{} {} {:?}", slot.key, upload.external_file_id, upload.upload_status);
    }
}

Tip

Polling is fine to get started, but webhooks are the intended path for production. Poll no more than a few times per minute and always prefer the webhook signal.

4. Register a webhook endpoint

Register an HTTPS endpoint once and AirCredit pushes every event to it — starting with the file.ready for the upload above. The response is the only time the endpoint's signing_secret is ever returned — store it immediately; you need it to verify deliveries.

curl -X POST https://api.aircredit.de/partner/v1/webhook_endpoints \
  -H "Authorization: Bearer $AIRCREDIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example-partner.com/aircredit/webhooks",
    "description": "prod receiver"
  }'
const endpoint = await client.createWebhookEndpoint({
  url: "https://example-partner.com/aircredit/webhooks",
  description: "prod receiver",
});

// Store endpoint.signingSecret now — it is never returned again.
await saveSecret(endpoint.id, endpoint.signingSecret);
endpoint = client.create_webhook_endpoint(
    url="https://example-partner.com/aircredit/webhooks",
    description="prod receiver",
)

# Store endpoint.signing_secret now — it is never returned again.
save_secret(endpoint.id, endpoint.signing_secret)
let endpoint = client
    .create_webhook_endpoint()
    .body_map(|b| b
        .url("https://example-partner.com/aircredit/webhooks")
        .description(Some("prod receiver".to_string())))
    .send()
    .await?
    .into_inner();

// Store endpoint.signing_secret now — it is never returned again.
save_secret(&endpoint.id, endpoint.signing_secret.as_deref())?;

5. Start the evaluation

Once the documents you care about are ready, start the evaluation. It runs asynchronously — agentically working through the template's checks and data fields to a summary verdict — see Evaluations for the full lifecycle.

curl -X POST https://api.aircredit.de/partner/v1/submissions/$SUBMISSION_ID/evaluations \
  -H "Authorization: Bearer $AIRCREDIT_API_KEY"
const evaluation = await client.startEvaluation({ submissionId: submission.id });
console.log(evaluation.id, evaluation.status); // -> "…", "queued"
evaluation = client.start_evaluation(submission_id=submission.id)
print(evaluation.id, evaluation.status)  # -> "…", "queued"
let evaluation = client
    .start_evaluation()
    .submission_id(&submission.id)
    .send()
    .await?
    .into_inner();
println!("{} {:?}", evaluation.id, evaluation.status);

6. Receive the result

Your endpoint now receives the story as it happens: file.ready when an upload finishes, evaluation.started, possibly a few evaluation.progress, and finally evaluation.completed carrying the outcome, recommendation, and risk band. Each delivery is the canonical event envelope; the X-Airtype-Signature header lets you verify it came from AirCredit. Respond with any 2xx within 30 seconds to acknowledge.

{
  "id": "evt_2f0d9c62-6f6e-4a7e-8f8e-2d5c7a1b0c3d",
  "type": "file.ready",
  "created_at": "2026-02-01T10:32:04Z",
  "api_client_id": "…",
  "data": {
    "upload": {
      "id": "…",
      "external_file_id": "payslip-jan",
      "external_submission_id": "loan-2026-000451",
      "upload_status": "ready",
      "file_id": "…"
    }
  }
}

Verifying the signature is a few lines in every language — see the Webhooks guide for the full verification snippet per SDK. For the full typed result — per-check verdicts, extracted fields, confidence, citations — fetch the evaluation itself: Reading the result.

Where to go next

  • Templates & customization — author templates, checks, and data fields.
  • Uploads — content types, idempotency, slot assignment, and re-uploads.
  • Evaluations — lifecycle, the typed result, confidence and citations.
  • Webhooks — the signature scheme, retries, rotation, and deduplication.
  • Errors — the error envelope and every code you might see.
  • API reference — try each call live against the console.

On this page