Salta ai contenuti
Inizia

Il tuo primo import, da un capo all'altro

La panoramica ti lascia un motore sano. Questa pagina è il resto delle prime due ore: entra un file, qualcuno lo rivede, e righe firmate arrivano sul tuo backend.

Non scrivi una UI di mapping e non dichiari lo schema nel codice host. Lo schema è un catalogo pubblicato. Il widget lo carica. Apply è un webhook firmato — è il default.

your page Mildport engine your backend
───────── ─────────────── ────────────
<mildport-import ingest → match → review
catalog-key="crm"> ─────────► ─────────► POST /apply
HMAC-signed rows

Servono due valori, entrambi dal tuo account Mildport:

Cosa Dove Dove va
Chiave di licenza Licenze — una volta all’emissione license-key del widget
Chiave pubblica Self-hosting (IMPORT_LICENSE_PUBLIC_KEY) il motore, già, se il preflight della panoramica è passato

Conferma che il motore accetti la chiave:

Terminal window
curl -H "Authorization: Bearer $LICENSE" \
https://imports.your-infra.example/api/import/v1/license/verify

Una buona risposta è "verified": true. Qualsiasi altra cosa — vedi Risoluzione dei problemi di deployment.

Lo schema vive nell’editor dei cataloghi/admin/catalogs sul motore, o <mildport-catalog-editor> nel tuo admin. Bozza i campi da una spec OpenAPI o un record di esempio, marca required quelli che la tua API rifiuterebbe vuoti, e pubblica. Dai al catalogo una chiave stabile (crm, contacts, …).

Fissa quella chiave sul widget. I cambi di schema dopo sono un publish nell’editor, non un release dell’host:

<mildport-import
api-base-url="https://imports.your-infra.example"
license-key="SIGNED_TENANT_KEY"
catalog-key="crm"
></mildport-import>

Un catalogo pubblicato e niente catalog-key va bene lo stesso — il widget lo adotta da solo. Fissa la chiave quando una pagina deve sempre importare in uno schema specifico.

Schema calcolato per utente a runtime, o versionato con l’app? È il percorso schema-come-codice. Saltalo finché non hai un motivo.

Apply è di default un POST firmato al tuo backend. Registra l’URL una volta; la risposta porta il segreto di firma esattamente una volta — conservalo.

Terminal window
curl -X POST https://imports.your-infra.example/api/import/v1/webhooks \
-H "Authorization: Bearer $LICENSE" \
-H "Content-Type: application/json" \
-d '{ "url": "https://api.your-app.example/mildport/apply" }'

Una prima occhiata senza backend? el.applyMode = 'browser' e leggi le righe da onResults. La produzione è il webhook. Dettagli: Eventi e apply e il riferimento webhook.

Apri la pagina, lascia un CSV (o un foglio disordinato, o un PDF con una tabella), conferma il mapping suggerito, sistema ciò che la griglia di review segnala, e apply.

Il widget parla con il motore per ingest e mapping. Non chiami ingest tu stesso, salvo costruire un flusso headless, senza widget.

Ogni consegna è firmata HMAC. Verifica il body grezzo prima di fidarti, rifiuta i timestamp stantii, e deduplica su x-import-delivery-id — i retry sono normali.

import { createHmac, timingSafeEqual } from 'node:crypto';
function verify(rawBody: string, header: string, secret: string): boolean {
const parts = Object.fromEntries(header.split(',').map(kv => kv.split('=')));
const expected = createHmac('sha256', secret).update(`${parts.t}.${rawBody}`).digest('hex');
const got = Buffer.from(parts.v1 ?? '', 'hex');
const exp = Buffer.from(expected, 'hex');
return got.length === exp.length && timingSafeEqual(got, exp);
}

Il body sono righe nella forma del catalogo (contact.email, …) più un mapping. Restituisci 2xx quando le hai persistite. Un 4xx è terminale; un 5xx viene ritentato. Payload e header: Webhook di apply.

  • Il widget ha emesso import-applied con un rowCount.
  • Il tuo handler ha loggato un deliveryId e scritto altrettante righe.
  • Una seconda consegna con lo stesso id è un no-op dal tuo lato.

Bloccato? Risoluzione dei problemi di deployment copre immagini che non fanno pull, 401 di licenza, CORS, e un pod che non diventa mai Ready.

Avanti: Configurazione · Licenze · Cataloghi