Buenno API Reference
The Buenno API s organized around REST. Our API has predictable resource-oriented URLs, accepts form-encoded request bodies, returns JSON-encoded responses, and uses standard HTTP response codes, authentication, and verbs.
Authentication
The Buenno API uses API keys to authenticate requests. To send any request to our api, include api-auth-token to the request header.
Your API keys carry many privileges, so be sure to keep them secure! Do not share your secret API keys in publicly accessible areas such as GitHub, client-side code, and so forth.
Get token info
Test that your api-auth-token is valid. Token info includes information of accessible forms.
GET /api/v02/token-info
# Include your api-auth-token to each request
curl --location --request GET 'https://webreport.buenno.fi/api/v02/token-info' \
--header 'api-auth-token: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
Response:
{
"api_token":
{
"name": "Example CRM token",
"forms": [
{
"id": 1,
"name": "Example Form 2021",
"company_name": "Example Company Ltd"
}
]
}
}
Invitations
Use following endpoints to invite users to answer a survey. Message content for email or SMS is created automatically by Buenno.
Create invitation
Invite customers to answer to a survey. Use either email, phone or deliver_externally to specify how user should receive the invitation. Add required context by filling metadata fields. If suitable field is missing, you can add your own key/value pairs as needed. We do not store empty strings or null values. All metadata values are returned as arrays.
POST /api/v02/invitations
curl --location --request POST 'https://webreport.buenno.fi/api/v02/invitations/' \
--header 'api-auth-token: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' \
--header 'Content-Type: application/json' \
--data '{
"invitation": {
"phone": "00358441234567",
"metadata": {
"first_name": "John",
"last_name": "Doe",
"product": ["ProductA", "ProductB"]
}
}
}'
Response
{
"invitation": {
"id": 1,
"email": null,
"phone": "00358441234567",
"question_form_id": 1,
"delivery_method": "delivered_by_sms",
"invitation_link": "https://buenno.fi/i/0b6a5ed887f3b89acd08",
"metadata": {
"first_name": ["John"],
"last_name": ["Doe"],
"visit_timestamp": null,
"location": null,
"employee_name": null,
"product_category": null,
"product": ["ProductA", "ProductB"],
"external_id": null,
"preferred_language": ["en"]
},
"scheduled_at": "2020-12-18T00:00:00+02:00",
"sent_at": "2020-12-18T00:00:00+02:00",
"opened_at": "2020-12-19T00:00:00+02:00",
"answered_at": null,
"created_at": "2020-12-18T00:00:00+02:00",
"updated_at": "2020-12-18T00:00:00+02:00"
}
}
Request Body
| Parameter | Description | Type | Mandatory |
|---|---|---|---|
| phone | Phone number to send survey invitation. Phone number should include country code where + is replaced with 00. E.g +358441234567 becomes 00358441234567 | String | Conditional |
| Email to send survey invitation | String | Conditional | |
| deliver_externally | If true, invitation is not sent by Buenno. An invitation can be created without email or phone | Boolean | Conditional |
| metadata | Additional customer information (use the fields below when applicable, or add your own key/value pairs as needed) | Object | No |
| • first_name | Customer first name | String / Array | No |
| • last_name | Customer last name | String / Array | No |
| • visit_timestamp | Time of the customer visit, purchase or interaction (ISO8601: "2025-10-02T06:25:16Z" utc, "2025-10-02T06:25:16+03:00" with timezone) | Timestamp / Array | No |
| • location | Name of the location where interaction happened. Single value only — arrays are not accepted for this key. | String | No |
| • location_external_id | Customer-supplied integer id for the location. Unique per customer when set. May be sent alongside location; if a different store already uses the same name with another location_external_id, the request is rejected with 422. |
Integer | No |
| • employee_name | Employee name | String / Array | No |
| • product_category | Product category | String / Array | No |
| • product | Product | String / Array | No |
| • external_id | External customer id | String / Array | No |
| • preferred_language | Language to use for this invitation, ISO 639‑1 code | String / Array | No |
POST /api/v02/invitations
curl --location --request POST 'https://webreport.buenno.fi/api/v02/invitations/' \
--header 'api-auth-token: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' \
--header 'Content-Type: application/json' \
--data '{
"invitation": {
"phone": "00358441234567",
"metadata": {
"location": "Helsinki",
"location_external_id": 245
}
}
}'
POST /api/v02/invitations
curl --location --request POST 'https://webreport.buenno.fi/api/v02/invitations/' \
--header 'api-auth-token: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' \
--header 'Content-Type: application/json' \
--data '{
"invitation": {
"deliver_externally": true
}
}'
Response
{
"invitation": {
"id": 1,
"email": null,
"phone": null,
"question_form_id": 1,
"delivery_method": "delivered_externally",
"invitation_link": "https://buenno.fi/i/0b6a5ed887f3b89acd08",
"metadata": {
"first_name": null,
"last_name": null,
"visit_timestamp": null,
"location": null,
"employee_name": null,
"product_category": null,
"product": null,
"external_id": null,
"preferred_language": ["en"]
},
"scheduled_at": "2020-12-18T00:00:00+02:00",
"sent_at": "2020-12-18T00:00:00+02:00",
"opened_at": "2020-12-19T00:00:00+02:00",
"answered_at": null,
"created_at": "2020-12-18T00:00:00+02:00",
"updated_at": "2020-12-18T00:00:00+02:00"
}
}
Exceptions
Returns 409 if invitation with the given identifier exists, or if the identifier is nonverifiable.
Returns 422 if validation fails (e.g. email or phone invalid, or metadata field value is invalid).
Returns 403 if no active question_form is present.
Returns 404 if question_form_id is invalid.
Response Fields
Both Create and List endpoints return invitation objects with the same shape.
| Field | Description | Type |
|---|---|---|
| id | Unique invitation id | Integer |
| Recipient email if email delivery was requested | String / null | |
| phone | Recipient phone if SMS delivery was requested | String / null |
| question_form_id | Id of the survey this invitation belongs to | Integer |
| delivery_method | Channel used. One of delivered_by_sms, delivered_by_email, delivered_externally. Null on freshly created SMS/email invitations until the sent |
String / null |
| invitation_link | Unique URL the recipient opens to answer the survey. | String |
| metadata | Customer metadata (see Metadata above). All values are returned as arrays. | Object |
| scheduled_at | When the invitation is scheduled for sending. | Timestamp / null |
| sent_at | When the invitation is actually sent. | Timestamp / null |
| opened_at | When the recipient first opened the invitation link. | Timestamp / null |
| answered_at | When the recipient submitted the reply. | Timestamp / null |
| created_at | When the invitation was created. | Timestamp |
| updated_at | Last modification time. | Timestamp |
All timestamps are ISO 8601 formatted in the server's local timezone (Europe/Helsinki, +02:00 in winter / +03:00 during DST).
Metadata
The metadata object accepts any keys you choose. Currently supported value types are string, integer, numeric, timestamp, and boolean.
When a new key is sent for the first time, the system infers its data type from the value and stores it. After that, the same key can no longer accept values of a different type.
A handful of common keys are pre-created (see the Request Body table above). All of them are of type string, except visit_timestamp, which is a timestamp.
Each value can be submitted as a single value or as an array of values of the same type, except for location and location_external_id, which accept single values only. All values are returned as arrays.
Metadata example
"metadata": {
"location": "Helsinki",
"product": ["ProductA", "ProductB"],
"order_total": 4999,
"visit_timestamp": "2025-10-02T06:25:16Z",
"is_returning_customer": true
}
Note that filtering on the reports is currently supported by the following predefined metadata keys:
visit_timestamplocation/location_external_idemployee_nameproduct_categoryproductexternal_id
List invitations
Lists created invitations.
GET /api/v02/invitations
curl --location --request GET 'https://webreport.buenno.fi/api/v02/invitations/?from=2020-12-17T00:00:00%2B02:00&to=2020-12-19T00:00:00%2B02:00&limit=100' \
--header 'api-auth-token: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
Response
{
"invitations": [
{
"id": 1,
"email": null,
"phone": "00358441234567",
"question_form_id": 1,
"delivery_method": "delivered_by_sms",
"invitation_link": "https://buenno.fi/i/0b6a5ed887f3b89acd08",
"metadata": {
"first_name": null,
"last_name": null,
"visit_timestamp": null,
"location": null,
"employee_name": null,
"product_category": null,
"product": null,
"external_id": null,
"preferred_language": ["en"]
},
"scheduled_at": "2020-12-18T00:00:00+02:00",
"sent_at": "2020-12-18T00:00:00+02:00",
"opened_at": "2020-12-19T00:00:00+02:00",
"answered_at": null,
"created_at": "2020-12-18T00:00:00+02:00",
"updated_at": "2020-12-18T00:00:00+02:00"
}
],
"total": "1",
"limit": "100",
"offset": "0"
}
URL Parameters
| Parameter | Description | Type | Mandatory |
|---|---|---|---|
| from | Return invitations created after this timestamp (exclusive) | Date time ISO 8601 | No |
| to | Return invitations created before this timestamp (exclusive) | Date time ISO 8601 | No |
| limit | Max number of rows to return, Default = 100, Max = 1000 | Integer | No |
| offset | Row number to start from. Default = 0 | Integer | No |
| question_form_id | Field to identify question form in a case when api-auth-token has access to more than one form. If not filled first active survey will be used. | Integer | No |
Replies (work in progress)
List
Get a list of replies and their answers.
GET /api/v02/replies
curl --location --request GET 'https://webreport.buenno.fi/api/v02/replies' \
--header 'api-auth-token: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' \
-d 'from=2020-12-17T00:00:00+00:00' \
-d 'to=2020-12-19T00:00:00+00:00' \
-d 'limit=1000'
Response
{
"replies": [
{
"id": 1,
"name": "Example Survey 12/2020",
"email": "",
"phone": "00358441234567",
"metadata": {
"first_name": ["John"],
"last_name": ["Doe"],
"visit_timestamp": ["2020-12-12T00:00:00Z"],
"location": null,
"employee_name": null,
"product_category": null,
"product": ["ProductA", "ProductB"],
"external_id": null,
"preferred_language": ["en"]
},
"question_count": 2,
"score_sum": 125,
"avg_score": 62.5,
"answers": [
{
"question": "Lorem ipsum",
"points": 50,
"answer": "Sed ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium doloremque laudantium.",
"question_id": 1234,
"question_type_id": 1
},
{
"question": "Lorem ipsum 2",
"points": 75,
"answer": null,
"question_id": 1237,
"question_type_id": 1
},
{
"question": "De finibus bonorum et malorum",
"points": 0,
"answer": "qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui do.",
"question_id": 2345,
"question_type_id": 13
}
],
"updated_at": "2020-12-19T00:00:00Z",
"created_at": "2020-12-18T00:00:00Z"
}
],
"total": "2225",
"limit": "500",
"offset": "0"
}
URL Parameters
| Parameter | Description | Type | Mandatory |
|---|---|---|---|
| from | Answers from date | Date time ISO 8601 | Yes |
| to | Answers to date | Date time ISO 8601 | Yes |
| limit | Max number of answers to return, Default = 500, Max = 1000 | Integer | No |
| offset | Answer number to start from. Default = 0 | Integer | No |
| locale | Locale used in question names | string | No |
Server is in UTC time. If you want to use local time for api calls use the +00:00 part to provide the offset to UTC.
Each reply contains question_count, score_sum, and avg_score, these 3 belong together:
| Name | Meaning |
|---|---|
| question_count | The amount of main questions that have points. |
| score_sum | The sum of all those main questions' points. |
| avg_score | The average score for this reply; score_sum / question_count. |
Each question includes a question_id and question_type_id. question_id is a unique identifier for that question while question_type_id indicates which kind of question it is. The possible question types are:
| question_type_id | Question type |
|---|---|
| 1 | Main question |
| 2 | Other question |
| 4 | Admin |
| 5 | Offered product |
| 6 | Shopping goal |
| 7 | Answerer |
| 8 | Age |
| 9 | Gender |
| 10 | Seller's name |
| 11 | Visit date |
| 12 | Visit time |
| 13 | NPS |
| 48 | Product category |
| 49 | Seller's ID |
| 52 | External rating (e.g. imported Google Review) |
Errors
The Buenno API uses following HTTP status codes on errors.
Example of json error response:
{
"error_message": "Invitation with the given identifier exists."
}
| Code | Possible cause |
|---|---|
| 401 | api-auth-token not found or invalid |
| 403 | Action is not allowed to perform right now. |
| 404 | Given id not found or invalid |
| 404 | Method (GET/PUT/POST/PATCH/DELETE) is not allowed |
| 409 | Conflict — given identifier already exists, or cannot be verified (see endpoint-specific notes) |
| 422 | Unprocessable entry (check error message for details) |