Skip to main content
POST
/
requests
curl -X POST 'https://connect.penbox.io/v1/requests' \
  -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "flow": {
      "slug": "client-onboarding"
    },
    "user": {
      "email": "[email protected]",
      "given_name": "John",
      "family_name": "Doe"
    },
    "data": {
      "company_name": "Acme Corp",
      "address_zip": "10001",
      "annual_revenue": "500000"
    },
    "external_args": {
      "order_id": "ORD-12345"
    },
    "redirect_url": "https://myapp.com/thank-you"
  }'
{
  "id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
  "created_at": "2023-11-07T05:31:56Z",
  "status": "draft",
  "archived": true,
  "archived_at": "2023-11-07T05:31:56Z",
  "processed_at": "2023-11-07T05:31:56Z",
  "active_from": "2023-11-07T05:31:56Z",
  "active_until": "2023-11-07T05:31:56Z",
  "links": {
    "fill": "<string>",
    "app": "<string>"
  },
  "user": {
    "anonymous": true,
    "email": "<string>",
    "phone": "<string>",
    "given_name": "<string>",
    "family_name": "<string>"
  },
  "owner": {
    "id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
    "email": "<string>"
  },
  "flow": {
    "slug": "<string>"
  },
  "data": {},
  "external_args": {},
  "options": {},
  "responses": [
    {
      "id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
      "completed_at": "2023-11-07T05:31:56Z",
      "declined_at": "2023-11-07T05:31:56Z",
      "data": {},
      "user": {
        "anonymous": true,
        "ip": "<string>",
        "email": "<string>",
        "phone": "<string>",
        "locale": "<string>",
        "given_name": "<string>",
        "family_name": "<string>",
        "user-agent": "<string>",
        "accept-language": "<string>"
      },
      "attachments": [
        {
          "id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
          "name": "<string>",
          "type": "<string>",
          "metadata": {
            "size": 123,
            "width": 123,
            "height": 123
          },
          "uri": "<string>"
        }
      ],
      "signatures": {},
      "files": {}
    }
  ],
  "notifications": [
    {
      "id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
      "status": "pending",
      "method": "sms",
      "at": "2023-11-07T05:31:56Z",
      "to": "<string>",
      "from": "<string>",
      "cc": "<string>",
      "bcc": "<string>",
      "system": true,
      "locale": "<string>",
      "template": "<string>",
      "variables": {},
      "active": true,
      "error": "<string>",
      "message_id": "<string>",
      "attachments": {},
      "deleted_at": "2023-11-07T05:31:56Z"
    }
  ]
}
In the API, forms are referred to as requests. This is the technical term used in all endpoints and parameters.
curl -X POST 'https://connect.penbox.io/v1/requests' \
  -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "flow": {
      "slug": "client-onboarding"
    },
    "user": {
      "email": "[email protected]",
      "given_name": "John",
      "family_name": "Doe"
    },
    "data": {
      "company_name": "Acme Corp",
      "address_zip": "10001",
      "annual_revenue": "500000"
    },
    "external_args": {
      "order_id": "ORD-12345"
    },
    "redirect_url": "https://myapp.com/thank-you"
  }'

Request Parameters

Required Fields

FieldTypeDescription
flow.slugstringForm template identifier

Optional Fields

FieldTypeDescription
company.slugstringWorkspace identifier (if multiple authorized)
user.emailstringContact email address
user.phonestringContact phone number
user.given_namestringContact first name
user.family_namestringContact last name
user.localestringContact language (e.g., en, fr)
dataobjectPre-filled form data (keys must match form field keys)
external_argsobjectYour custom metadata
redirect_urlstringHTTPS URL to redirect after completion
draftbooleanCreate as draft (default: false)
active_fromstringISO 8601 date when form becomes active
active_untilstringISO 8601 date when form expires
owner.emailstringForm owner email address
branding.slugstringCustom branding identifier

Form Statuses

StatusDescription
draftForm created but not yet sent
pendingForm sent, waiting for completion
completedContact submitted the form
declinedContact declined to fill the form
processedResponse has been reviewed/processed

Pre-filling Form Data

The data parameter allows you to pre-fill form fields with values. This is useful for importing data from your system or providing a better user experience by reducing the amount of information the contact needs to enter.

How It Works

The keys in the data object must exactly match the field keys defined in your form template. When a contact opens the form, these fields will already contain the provided values. Example: If your form template has fields with keys:
  • company_name
  • address_zip
  • annual_revenue
You can pre-fill them like this:
{
  "data": {
    "company_name": "Acme Corp",
    "address_zip": "10001",
    "annual_revenue": "500000"
  }
}

Finding Field Keys

Field keys are defined in your form template configuration in Penbox. Common examples:
Field TypeExample Keys
Company Infocompany_name, company_vat, company_registration
Addressaddress_street, address_city, address_zip, address_country
Contactcontact_name, contact_email, contact_phone
Financialannual_revenue, number_employees, bank_account
CustomAny custom field keys you’ve defined in your template
Field keys are case-sensitive and must match exactly. Use the Penbox app or contact your administrator to find the correct field keys for your form template.

Complete Example

const response = await fetch('https://connect.penbox.io/v1/requests', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${accessToken}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    flow: { slug: 'client-onboarding' },
    user: {
      email: '[email protected]',
      given_name: 'John',
      family_name: 'Doe'
    },
    // Pre-fill form fields
    data: {
      company_name: 'Acme Corporation',
      company_vat: 'BE0123456789',
      address_street: '123 Main Street',
      address_city: 'New York',
      address_zip: '10001',
      address_country: 'US',
      annual_revenue: '1500000',
      number_employees: '25'
    },
    external_args: {
      crm_id: '12345'
    }
  })
});

Use Cases

Import from CRM:
// Pull data from your CRM and pre-fill the form
const crmContact = await getCRMContact(contactId);

const formData = {
  company_name: crmContact.companyName,
  address_zip: crmContact.zipCode,
  annual_revenue: crmContact.revenue,
  contact_phone: crmContact.phone
};

await createRequest({ 
  flow: { slug: 'onboarding' },
  user: { email: crmContact.email },
  data: formData 
});
Progressive Forms:
// Pre-fill with data collected in previous steps
const formData = {
  company_name: step1Data.company,
  address_zip: step2Data.zip,
  business_type: step3Data.type
};
Reduce Friction:
// Pre-fill known information to make the form faster
const formData = {
  company_name: user.organization,
  contact_email: user.email,
  contact_phone: user.phone
};
Pre-filling data improves form completion rates and reduces errors. Always pre-fill what you know while allowing contacts to modify values if needed.
If you provide a field key that doesn’t exist in the form template, it will be silently ignored. Make sure your field keys match your template configuration.

External Args

Use external_args to store your own metadata with forms for easy tracking:
{
  "external_args": {
    "crm_id": "12345",
    "order_id": "ORD-789",
    "source": "website",
    "campaign": "summer-2024"
  }
}
You can filter by external_args when listing forms:
{
  "filter": {
    "external_args": {
      "order_id": "ORD-789"
    }
  }
}

Response Codes

CodeDescription
201Created - New form created successfully
400Bad Request - Invalid parameters
401Unauthorized - Invalid access token
403Forbidden - No access to this resource
409Conflict - Multiple form templates match criteria
422Unprocessable - Validation failed
429Too Many Requests - Rate limit exceeded
500Server Error - Internal error
Created forms are immediately active unless draft: true is specified. Draft forms must be activated by setting draft: false via PATCH.
Use external_args to link Penbox forms back to your system’s entities (orders, customers, tickets, etc.). This makes it easy to track and manage forms in your system.
The active_from and active_until dates control when the form link is accessible. Set active_until to create expiring forms.

Authorizations

Authorization
string
header
required

OAuth2 access token. Include as: Authorization: Bearer {access_token}

Body

application/json
flow
object
required
company
object
user
object
data
object

Pre-filled form data

external_args
object

Your custom metadata

redirect_url
string<uri>

HTTPS URL to redirect after completion

draft
boolean
default:false
archived
boolean
default:false

Response

201 - application/json

Request created successfully

id
string<uuid>

Request UUID

created_at
string<date-time>

Creation timestamp

status
enum<string>

Current request status

Available options:
draft,
pending,
completed,
declined,
processed
archived
boolean

Whether the request is archived

archived_at
string<date-time> | null

Timestamp when archived

processed_at
string<date-time> | null

Timestamp when marked as processed

active_from
string<date-time> | null

When the form becomes active

active_until
string<date-time> | null

When the form expires

URLs for accessing the request

user
object

Contact information

owner
object

Request owner information

flow
object

Form template information

data
object

Pre-filled form data

external_args
object

Custom metadata

options
object

Form-specific options

responses
object[]

Array of form responses

notifications
object[]

Array of notifications sent for this request