# Formular für Angebotsanfragen ohne Datei-Uploads bauen

> Ein Formular für Angebotsanfragen, das Briefings als geteilte Links statt als Uploads sammelt, jeder Anfrage ein Fälligkeitsdatum gibt und linklastige Leads aus dem Spam hält.

Veröffentlicht am 2026-10-25 · https://ikiform.com/de/guides/lead-capture/quote-request

Frag in einem `url`-Feld nach dem Briefing als Link zu einem geteilten Ordner, denn Ikiform lehnt Datei-Uploads mit `422 files_not_supported` ab. Halte Leistung und Budget als feste Auswahl, damit jede Anfrage in derselben Form ankommt, und versprich in der Erfolgsmeldung eine Bearbeitungszeit. Schick dann jede Anfrage mit angehängtem Fälligkeitsdatum ans Team, damit das Versprechen gehalten wird.

## Was an einer Angebotsanfrage anders ist

Eine Angebotsanfrage kommt mit Material: ein Briefing, ein Grundriss, eine Tabelle mit Teilen, das Design vom letzten Jahr. Die übliche Antwort ist ein Datei-Input. Der funktioniert hier nicht, und es lohnt sich zu wissen, warum, bevor du darum herum planst.

Datei-Uploads werden noch nicht unterstützt. Ein Post mit einer Datei bekommt `422 files_not_supported`. Abgelehnt wird der ganze Post, nicht nur die Datei, also verliert ein Besucher, der ein PDF anhängt, alles Eingetippte, außer dein Formular zeigt den Fehler und behält seine Eingaben. Ein geteilter Link umgeht das Problem und hat eigene Vorteile: Der Kunde kann nach dem Senden Dateien ergänzen, es gibt kein Größenlimit, und nichts Großes liegt in deinem Posteingang.

Diagramm: Ein Datei-Input im Vergleich zu einem Link-Feld in einem Formular für Angebotsanfragen

1. Mit einem Datei-Input hängt der Besucher brief.pdf an, und der Browser sendet einen Multipart-Post.
2. Ikiform lehnt den ganzen Post mit 422 files_not_supported ab.
3. Nichts wird gespeichert, also geht weder E-Mail noch Webhook raus.
4. Mit einem Link-Feld fügt der Besucher einen Link zu einem geteilten Ordner ein.
5. Ikiform prüft, ob es eine gültige https-URL ist, und speichert die Anfrage.
6. Der Link kommt in der Benachrichtigungs-E-Mail und im Chat an, wo ihn jeder im Team öffnen kann.

Gleiche Anfrage, zwei Ergebnisse. Frag nach einem Ordner-Link, und die Dateien gehen nie durch das Formular.

## Die Felder

Die [Vorlage für Angebotsanfragen](https://ikiform.com/forms/quote-request) hat den vollständigen Feldsatz mit Telefon, Unternehmen und Zeitplan. Die Version unten kürzt ihn und ergänzt das Link-Feld. Probier einen `http:`-Link oder einen nackten `drive.google.com`-Link, um zu sehen, wie das `url`-Feld antwortet.

Formular: Quote request

- **Name** (`name`, text, Pflichtfeld)
- **Geschäftliche E-Mail** (`email`, email, Pflichtfeld)
- **Was brauchst du?** (`service`, select, Pflichtfeld)
- **Budget** (`budget`, select)
- **Link zu deinen Dateien** (`files_url`, url)
- **Was soll das Angebot abdecken?** (`details`, textarea, Pflichtfeld)

`files_url` ist ein `url`-Feld mit `https_only`, also wird ein nacktes `drive.google.com/...` als `https://drive.google.com/...` gespeichert und ein `http:`-Link abgelehnt.

| Feld | Warum es so aufgebaut ist |
| --- | --- |
| `files_url` | Ein Link zu einem Ordner, nicht ein Feld pro Datei. Der Kunde bestimmt, was drin ist, und kann später ergänzen |
| `service` | Entscheidet, wer das Angebot kalkuliert. Die Werte bleiben fürs Routing stabil, auch wenn du die Labels umbenennst |
| `budget` | Optional, mit einer Auswahl **Not sure yet**, die nichts sendet. Ein Pflichtfeld für das Budget kostet Leads |
| `details` | Pflicht, mindestens 20 Zeichen. Ein Angebot braucht einen Umfang, und hier kommt er her |

## Das Formular

Reines HTML. `type="url"` bringt auf Smartphones die passende Tastatur, und der Hilfetext sagt, wie man den Ordner teilt, was eine Rückfrage spart, wenn sich der erste Link als privat herausstellt.

`quote.html`:

```html
<form action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="POST">
  <label for="name">Name</label>
  <input id="name" name="name" autocomplete="name" required />

  <label for="email">Work email</label>
  <input id="email" name="email" type="email" autocomplete="email" required />

  <label for="service">What do you need?</label>
  <select id="service" name="service" required>
    <option value="design">Design</option>
    <option value="development">Development</option>
    <option value="both">Design and development</option>
  </select>

  <label for="budget">Budget</label>
  <select id="budget" name="budget">
    <option value="">Not sure yet</option>
    <option value="under_5k">Under $5,000</option>
    <option value="5k_20k">$5,000 to $20,000</option>
    <option value="over_20k">Over $20,000</option>
  </select>

  <label for="files_url">Link to your files</label>
  <input id="files_url" name="files_url" type="url" inputmode="url" placeholder="https://drive.google.com/..." aria-describedby="files-help" />
  <p id="files-help">A shared folder with the brief, mockups or specs. Set it so anyone with the link can view.</p>

  <label for="details">What should the quote cover?</label>
  <textarea id="details" name="details" rows="5" minlength="20" required></textarea>

  <input type="hidden" name="_redirect" value="https://example.com/quote/thanks" />
  <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
  <button>Request a quote</button>
</form>
```

> Entferne jedes `<input type="file">`, das du aus einem alten Formular geerbt hast. Ein leeres wird übersprungen. Ist eine Datei gewählt, scheitert ein Multipart- oder `FormData`-Post als Ganzes, und ein einfaches urlencoded Formular sendet nur den Dateinamen. So oder so kommt die Datei nie an.

## Links und der Spam-Score

Ein Angebotsformular ist der eine Ort, an dem echte Leads absichtlich Links senden, und Links sind ein Spam-Signal. Drei oder mehr Links über alle Felder erhöhen den Score um 2. Ein `url`-Feld wird als vollständiger `https://`-Link gespeichert, zählt also mit.

| Die Anfrage enthält | Score | Ergebnis |
| --- | --- | --- |
| Den Ordner-Link | 0 | Empfangen |
| Den Ordner-Link und zwei weitere Links in `details` | +2 | Empfangen |
| Dieselben drei Links, innerhalb von 24 Stunden mit denselben Feldern erneut gesendet | +2 und +3 | Spam, bei 5 |

Die dritte Zeile ist eine echte Person, die dachte, das erste Absenden sei fehlgeschlagen. Deshalb sollte das Formular ein Link-Feld haben, nicht drei, und deshalb muss der Erfolgszustand unmissverständlich sein. Prüf den Filter **Spam** wöchentlich; markierst du eine Anfrage als kein Spam, werden dann ihre E-Mail und ihr Webhook gesendet.

## Jeder Anfrage ein Fälligkeitsdatum geben

Die Erfolgsmeldung verspricht ein Angebot innerhalb von zwei Werktagen. Dieser Cloudflare Worker macht aus dem Versprechen ein Datum, das das Team sieht. Er verifiziert den Webhook, berechnet das Fälligkeitsdatum aus `created_at` und postet die Anfrage mit dem Datei-Link in einen Angebots-Channel:

`src/index.ts`:

```ts
import { verifyWebhook, WebhookVerificationError } from "@ikiform/sdk/webhooks";

function addWorkingDays(from: Date, days: number) {
  const date = new Date(from);
  while (days > 0) {
    date.setUTCDate(date.getUTCDate() + 1);
    const day = date.getUTCDay();
    if (day !== 0 && day !== 6) days -= 1;
  }
  return date.toISOString().slice(0, 10);
}

export default {
  async fetch(request: Request, env: { IKIFORM_WEBHOOK_SECRET: string; SLACK_QUOTES_WEBHOOK: string }) {
    const payload = await request.text();
    let event;
    try {
      event = await verifyWebhook({ payload, headers: request.headers, secret: env.IKIFORM_WEBHOOK_SECRET });
    } catch (error) {
      if (error instanceof WebhookVerificationError) return new Response(error.reason, { status: 400 });
      throw error;
    }
    if (event.type !== "submission.created") return new Response(null, { status: 204 });

    const quote = event.data.fields;
    const due = addWorkingDays(new Date(event.data.created_at), 2);
    const lines = [
      `Quote due *${due}*: ${quote.service} for ${quote.name} (${quote.budget ?? "budget not given"})`,
      quote.files_url ? `Files: ${quote.files_url}` : "No files linked. Ask for them in the first reply.",
      `Reply: mailto:${quote.email}`,
    ];

    const response = await fetch(env.SLACK_QUOTES_WEBHOOK, {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ text: lines.join("\n") }),
    });
    if (!response.ok) return new Response(`Slack answered ${response.status}`, { status: 502 });
    return new Response(null, { status: 204 });
  },
};
```

- Werktage überspringen Samstag und Sonntag in UTC. Ergänze deine Feiertage, wenn du sie beim Angebot berücksichtigst.
- Ein fehlendes `files_url` erzeugt eine Erinnerung, in der ersten Antwort nach Dateien zu fragen, statt einer leeren Zeile.
- Mit `502` zu antworten, wenn Slack fehlschlägt, lässt Ikiform dieselbe Zustellung erneut versuchen, sodass keine Anfrage verloren geht.

Füge die URL des Workers unter **Webhooks** hinzu, kopiere das `whsec_`-Secret in `IKIFORM_WEBHOOK_SECRET` und speichere die Slack-URL als Secret. Die [Slack-Anleitung](https://ikiform.com/blog/form-submissions-to-slack.md) behandelt das Anlegen des Incoming Webhooks.

## Prüfen, ob es funktioniert

1. Sende mit einem Ordner-Link von Google Drive oder Dropbox. Öffne den Link aus der Benachrichtigungs-E-Mail in einem privaten Fenster, um zu prüfen, dass er sichtbar ist.
2. Sende mit einem `http:`-Link. Der Post wird mit `422` abgelehnt, und die Meldung nennt `files_url`.
3. Durchsuche deine Vorlagen nach `type="file"` und entferne jeden Treffer aus Angebotsformularen, die an Ikiform senden.
4. Prüf den Angebots-Channel. Die Nachricht zeigt das Fälligkeitsdatum zwei Werktage später und den Datei-Link.
5. Drück **Reply** in der Benachrichtigungs-E-Mail. Sie schreibt an die Adresse aus dem Formular.

## Stolperfallen

- **Private Links.** Der häufigste Fehler ist ein Link, den nur sein Besitzer öffnen kann. Sag im Hilfetext, wie man ihn teilt, und bitte in der ersten Antwort um Zugriff, statt darauf zu warten.
- **Kunden mit Dateien, aber ohne Ordner.** Bitte sie, auf deine erste E-Mail mit Anhängen zu antworten. Reply-To bringt ihre Dateien in einen normalen E-Mail-Verlauf mit deinem Team.
- **Lange Umfänge.** Benachrichtigungs-E-Mails zeigen Werte bis zu 500 Zeichen. Der vollständige `details`-Text steht im Dashboard und im Webhook.
- **Keine Eingangsbestätigung per E-Mail.** Ikiform schreibt der Person, die das Formular ausfüllt, keine E-Mail. Die Meldung auf der Seite ist die Bestätigung, also mach die Bearbeitungszeit konkret.

## Fragen

### Kann das Formular Datei-Uploads annehmen?

Noch nicht. Ein Post mit einer Datei wird mit 422 files_not_supported abgelehnt. Bitte stattdessen um einen Link zur Datei, etwa die URL eines geteilten Laufwerks.

### Wie sammle ich Zeichnungen, Briefings oder Fotos mit einem Angebotsformular?

Frag in einem url-Feld nach einem Link zu einem geteilten Ordner und sag im Hilfetext, dass der Ordner für jeden mit dem Link sichtbar sein muss. Der Kunde kann nach dem Senden der Anfrage Dateien in den Ordner legen.

### Warum ist eine Angebotsanfrage mit mehreren Links im Spam gelandet?

Drei oder mehr Links erhöhen den Spam-Score um 2, und dieselben Felder, innerhalb von 24 Stunden erneut gesendet, um 3, was den Schwellenwert von 5 erreicht. Behalte ein Link-Feld und markiere die Anfrage als kein Spam, um ihre E-Mail und ihren Webhook zu senden.

### Kann ich das Budget-Feld optional machen?

Ja. Lass required weg und füge eine erste Option mit leerem Wert hinzu, etwa „Noch unklar“. Eine leere Auswahl sendet nichts, und Ikiform speichert die Anfrage ohne Budget.

## Weitere Anleitungen

- [Formular zur Lead-Erfassung](https://ikiform.com/de/guides/lead-capture.md): Ein Lead-Formular auf deiner eigenen Site, das dich per E-Mail mit Reply-To benachrichtigt, den Team-Chat anpingt und dein CRM füttert, damit du antwortest, solange der Lead noch da ist.
- [Formular für Projektanfragen](https://ikiform.com/de/guides/lead-capture/project-inquiry.md): Ein Formular für neue Projekte für Studios und Freelancer, das Anfragen nach Budget und Zeitpunkt in heiß, warm und unpassend sortiert, bevor jemand das Briefing liest.
- [Formular für Demo-Anfragen](https://ikiform.com/de/guides/lead-capture/demo-request.md): Ein Formular zum Demo-Buchen, das den Lead speichert und den Besucher dann mit vorausgefülltem Namen und E-Mail zu Cal.com oder Calendly schickt.
- [Leads an HubSpot oder Pipedrive](https://ikiform.com/de/guides/lead-capture/leads-to-crm.md): Bilde jeden signierten Ikiform-Webhook auf HubSpots Kontakt-Upsert oder die Personen- und Lead-Aufrufe von Pipedrive ab, ohne doppelte Kontakte bei einer wiederholten Zustellung.
- [Formular für Beratungsanfragen](https://ikiform.com/de/guides/lead-capture/consultation-request.md): Ein Formular für Beratungsanfragen für Steuerberater, Anwälte, Coaches und Praxen, das fragt, wie und wann angerufen werden soll, und heikle Details für das Gespräch lässt.
- [Lead-Formular mit Next.js](https://ikiform.com/de/guides/lead-capture/nextjs.md): Ein Lead-Formular für den Next.js App Router, das in einer Server Action mit Zod validiert, UTMs des ersten Kontakts in einem Cookie hält und qualifizierte Leads in deinen Kalender schickt.
