Перейти к содержимому

User submissions — submit()

Form skills (reviews, callback requests, subscriptions, orders) save visitor data via the single method window.ParsewiseSkillAPI.submit(). Anything you send appears in the shop admin on the “Submissions” tab of that specific skill — with a table, filters, CSV export and CRM-ready metadata.

How it works

1. Skill author declares form fields in SKILL.md (the submission_schema block).
2. The widget (drawer.js) collects data and calls Parsewise.submit(slug, data, opts?).
3. The server validates, writes to skill_submissions, runs declared on_submit actions (newsletter subscribe, shop notification, etc.) and returns the result.
4. The shop admin sees the record in the skill’s “Submissions” tab and can download CSV, process manually, or wait for CRM export.
METHOD

ParsewiseSkillAPI.submit(slug, data, opts?)

The primary method for submitting form data. Always returns a Promise that resolves — network and server errors come back as { ok: false, error } to keep the skill UI simple.
type SubmitResult = {
  ok: boolean
  id?: number              // DB id of the submission row
  submissionId?: string    // idempotency key you passed in (or null)
  actions?: Record<string, unknown>  // results from on_submit (e.g. { newsletter_subscribed: true })
  error?: string
}

window.ParsewiseSkillAPI.submit(
  slug: string,
  data: Record<string, unknown>,
  opts?: {
    submissionId?: string              // idempotency — same id returns existing row
    mediaUrls?: string[]               // optional list of uploaded file URLs
    sourceMeta?: Record<string, unknown>  // UTM, custom tags; shop proxy adds fingerprint itself
  }
): Promise<SubmitResult>

Minimal example — contact form

// drawer.js
const res = await window.ParsewiseSkillAPI.submit('contact-form', {
  name: state.name,
  email: state.email,
  text: state.message,
})

if (res.ok) {
  showThankYou()
} else {
  console.warn('Submit failed:', res.error)
}

Example with idempotency and media (review)

If the user hits submit twice, the server returns the existing record instead of a duplicate. To enable this, pass submissionId — a unique key generated on the client.
const reviewId = 'rv-' + Date.now().toString(36) + '-' + Math.random().toString(36).slice(2, 8)

await window.ParsewiseSkillAPI.submit(
  'emoji-review',
  {
    name: state.name,
    email: state.email,
    rating: state.rating,        // 'bad' | 'neutral' | 'good'
    text: state.text,
  },
  {
    submissionId: reviewId,
    mediaUrls: state.uploadedKeys,  // R2 keys returned by upload-url flow
  }
)

Example with full response and server-side actions

The “Promo for subscription” skill declares on_submit.subscribe_newsletter in SKILL.md. After submit the server adds the email to the newsletter and returns a flag in actions.
const res = await window.ParsewiseSkillAPI.submit('promo-code', {
  name: state.name,
  email: state.email,
  promo: chosenPromo.code,
})

console.log(res)
// {
//   ok: true,
//   id: 174,
//   submissionId: null,
//   actions: { newsletter_subscribed: true, already: false }
// }

Field declaration in SKILL.md

The admin UI and CSV export are built automatically from the skill’s submission_schema manifest block.
# SKILL.md
---
schema_version: 1
slug: emoji-review
version: 1.1.0
title: Оставить отзыв
# ...author, requires, triggers...
submission_schema:
  title:
    ru: "Отзывы"
    en: "Reviews"
  fields:
    - { key: name,   type: string, primary: true, label: { ru: "Имя",    en: "Name" },   csv: true }
    - { key: email,  type: email,                 label: { ru: "Email",  en: "Email" },  csv: true }
    - key: rating
      type: enum
      label: { ru: "Оценка", en: "Rating" }
      options: [bad, neutral, good]
      labels:
        bad:     { ru: "Плохо",     en: "Bad" }
        neutral: { ru: "Нормально", en: "Neutral" }
        good:    { ru: "Отлично",   en: "Good" }
      csv: true
    - { key: text,   type: text,  label: { ru: "Текст",  en: "Text" }, truncate: 120, csv: true }
    - { key: media,  type: media, label: { ru: "Медиа",  en: "Media" }, csv: false }
  csv:
    filename: "reviews-{date}.csv"
  on_submit:
    - action: subscribe_newsletter
      email_field: email
      name_field: name
      source: "skill:emoji-review"
  integrations:
    amocrm:
      lead_name: "Отзыв от {{name}}"
      tags: ["review", "{{rating}}"]
      fields:
        email: "{{email}}"
        note:  "{{text}}"
---

Field types

string — plain string.
email — email (validated on the client).
text — long text with optional truncate: N in the table.
enum — one of options; readable captions in labels.
number — number.
date — ISO string.
bool — yes/no.
media — array of file URLs; counter in the table, gallery in the details dialog.

Field flags

primary: true — the column comes first and is available as {{key}} in CRM templates.
csv: false — excludes the field from CSV export.
truncate: N — shortens the value in the list (full text lives in the details dialog).
label — string or {ru, en} localization object.

Built-in on_submit actions

Declarative actions that the server runs after the submission is saved. An individual action failure does not block the submit — results come back in res.actions.

subscribe_newsletter

Subscribes the given email to the shop newsletter. Used by skills like “Promo for subscription”, “Subscribe for news” and similar.
on_submit:
  - action: subscribe_newsletter
    email_field: email          # default "email"
    name_field: name            # optional
    source: "skill:my-skill"    # free-form tag
Returns in actions:
{ "newsletter_subscribed": true, "already": false }
METHOD

ParsewiseSkillAPI.verifyEmailSend(email, purpose?)

Sends a 6-digit verification code to an email. Universal endpoint, works for any skill. The code is included in the subject line so Safari / iOS Mail can autofill it as a one-time-code.
const res = await window.ParsewiseSkillAPI.verifyEmailSend(state.email, 'review')
// { ok: true, verified: false, message: 'Code sent' }
//   или { ok: true, verified: true } если email уже проверен в последние 24ч
METHOD

ParsewiseSkillAPI.verifyEmailCheck(email, code)

Checks the code. On success the server marks the email as verified for 24 hours — subsequent sends to this email within that window return verified: true without another email.
const res = await window.ParsewiseSkillAPI.verifyEmailCheck(state.email, state.code)
if (res.ok && res.verified) {
  // proceed with submit()
}

Example: multi-step form with verification

// 1. User enters email
const sendRes = await window.ParsewiseSkillAPI.verifyEmailSend(email)
if (!sendRes.ok) {
  showError(sendRes.message)
  return
}
if (sendRes.verified) {
  // Already verified recently — skip code step
  state.emailVerified = true
} else {
  showCodeInput()
}

// 2. User enters code
const checkRes = await window.ParsewiseSkillAPI.verifyEmailCheck(email, code)
if (!checkRes.ok || !checkRes.verified) {
  showError(checkRes.message)
  return
}

// 3. User fills the rest, submit
const result = await window.ParsewiseSkillAPI.submit('contact-form', {
  name: state.name,
  email,
  text: state.message,
})

if (result.ok) {
  showThankYou(result.actions)
}

Where to view submissions

The shop owner opens the admin: Skills → the skill → “Submissions” tab.
Columns come from submission_schema.fields; there are filters, bulk delete, a details dialog with full JSON and media gallery, and a “Download CSV” button.
“Export to amoCRM” and “Export to Bitrix24” buttons appear automatically when a skill declares the integrations block — currently disabled, enabled when CRM sinks ship.

Legacy endpoints and compatibility

Legacy endpoints POST /v1/public/shops/{id}/reviews and POST /v1/budget-request still work and dual-write into skill_submissions so shops with v1.0.0 skills don't lose data. Upgrade to skill versions that use Parsewise.submit() when you can.

Was this page helpful?