curl --request POST \
--url https://api.widgetic.com/embed/compositions/{compositionId}/entries/{table} \
--header 'Content-Type: application/json' \
--data '
{
"values": {},
"context": {},
"writeToken": "Example writeToken"
}
'import requests
url = "https://api.widgetic.com/embed/compositions/{compositionId}/entries/{table}"
payload = {
"values": {},
"context": {},
"writeToken": "Example writeToken"
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({values: {}, context: {}, writeToken: 'Example writeToken'})
};
fetch('https://api.widgetic.com/embed/compositions/{compositionId}/entries/{table}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.widgetic.com/embed/compositions/{compositionId}/entries/{table}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'values' => [
],
'context' => [
],
'writeToken' => 'Example writeToken'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.widgetic.com/embed/compositions/{compositionId}/entries/{table}"
payload := strings.NewReader("{\n \"values\": {},\n \"context\": {},\n \"writeToken\": \"Example writeToken\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.widgetic.com/embed/compositions/{compositionId}/entries/{table}")
.header("Content-Type", "application/json")
.body("{\n \"values\": {},\n \"context\": {},\n \"writeToken\": \"Example writeToken\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.widgetic.com/embed/compositions/{compositionId}/entries/{table}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"values\": {},\n \"context\": {},\n \"writeToken\": \"Example writeToken\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {}
}{
"success": true,
"message": "Example message",
"code": "NOT_FOUND",
"details": {},
"correlationId": "Example correlationId",
"error": {}
}{
"success": true,
"message": "Example message",
"code": "NOT_FOUND",
"details": {},
"correlationId": "Example correlationId",
"error": {}
}{
"success": true,
"message": "Example message",
"code": "NOT_FOUND",
"details": {},
"correlationId": "Example correlationId",
"error": {}
}{
"success": true,
"message": "Example message",
"code": "NOT_FOUND",
"details": {},
"correlationId": "Example correlationId",
"error": {}
}{
"success": true,
"message": "Example message",
"code": "NOT_FOUND",
"details": {},
"correlationId": "Example correlationId",
"error": {}
}{
"success": true,
"message": "Example message",
"code": "NOT_FOUND",
"details": {},
"correlationId": "Example correlationId",
"error": {}
}{
"success": true,
"message": "Example message",
"code": "NOT_FOUND",
"details": {},
"correlationId": "Example correlationId",
"error": {}
}{
"success": true,
"message": "Example message",
"code": "NOT_FOUND",
"details": {},
"correlationId": "Example correlationId",
"error": {}
}Append a visitor entry through the gateway
Public append-only write into a published composition database. No API key. The browser must send a permitted Origin header: the artifact iframe origin, or any website when the owner has not restricted the allowed websites (when the owner has, one of the websites the owner listed, Widgetic’s own sites, the visitor’s own machine or the owner’s own platform). When the request comes from the shared artifact origin, it must also carry the per-composition write token (X-Widgetic-Write-Token header or writeToken body field, handed to the widget in widgetic:host as entries.writeToken and returned as entryWriteToken on getPublicCompositionEmbed). Host context is stored for partner attribution; it is not authenticated unless the partner signs it (see Signed context). Request bodies are limited to 16 KB before parsing.
Address. https://api.widgetic.com/embed/compositions/{compositionId}/entries/{table}: the root of the API host, NOT under /v1. A client generated from this description for https://api.widgetic.com/v1 must be given the base address https://api.widgetic.com for the calls of the Public Embed tag.
Signed context. A partner that wants to trust context (to bill a customer by it, say) makes a signing secret (rotatePartnerEntryContextSecret), and its server signs the context it hands to the embed. The signature travels inside context as two reserved keys, widgetic_exp (UTC seconds, at most seven days ahead) and widgetic_sig. The gateway checks them after the Origin and before anything else is judged, stored or announced, and keeps and announces the context WITHOUT them. A context that is not signed as the partner requires is refused with 403 CONTEXT_SIGNATURE_REQUIRED, CONTEXT_SIGNATURE_INVALID or CONTEXT_SIGNATURE_EXPIRED; when the check cannot be made just now the answer is a retryable 503 CONTEXT_SIGNATURE_UNAVAILABLE, never an unchecked entry. Until a partner makes a secret nothing changes: the two keys are dropped and the rest is taken as it came. A signature is good until its widgetic_exp and may be used again until then, so a partner that must count each submission once keeps a unique value of its own (a submissionId, say) in the context. The recipe and a test vector are in the description of rotatePartnerEntryContextSecret.
curl --request POST \
--url https://api.widgetic.com/embed/compositions/{compositionId}/entries/{table} \
--header 'Content-Type: application/json' \
--data '
{
"values": {},
"context": {},
"writeToken": "Example writeToken"
}
'import requests
url = "https://api.widgetic.com/embed/compositions/{compositionId}/entries/{table}"
payload = {
"values": {},
"context": {},
"writeToken": "Example writeToken"
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({values: {}, context: {}, writeToken: 'Example writeToken'})
};
fetch('https://api.widgetic.com/embed/compositions/{compositionId}/entries/{table}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.widgetic.com/embed/compositions/{compositionId}/entries/{table}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'values' => [
],
'context' => [
],
'writeToken' => 'Example writeToken'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.widgetic.com/embed/compositions/{compositionId}/entries/{table}"
payload := strings.NewReader("{\n \"values\": {},\n \"context\": {},\n \"writeToken\": \"Example writeToken\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.widgetic.com/embed/compositions/{compositionId}/entries/{table}")
.header("Content-Type", "application/json")
.body("{\n \"values\": {},\n \"context\": {},\n \"writeToken\": \"Example writeToken\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.widgetic.com/embed/compositions/{compositionId}/entries/{table}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"values\": {},\n \"context\": {},\n \"writeToken\": \"Example writeToken\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {}
}{
"success": true,
"message": "Example message",
"code": "NOT_FOUND",
"details": {},
"correlationId": "Example correlationId",
"error": {}
}{
"success": true,
"message": "Example message",
"code": "NOT_FOUND",
"details": {},
"correlationId": "Example correlationId",
"error": {}
}{
"success": true,
"message": "Example message",
"code": "NOT_FOUND",
"details": {},
"correlationId": "Example correlationId",
"error": {}
}{
"success": true,
"message": "Example message",
"code": "NOT_FOUND",
"details": {},
"correlationId": "Example correlationId",
"error": {}
}{
"success": true,
"message": "Example message",
"code": "NOT_FOUND",
"details": {},
"correlationId": "Example correlationId",
"error": {}
}{
"success": true,
"message": "Example message",
"code": "NOT_FOUND",
"details": {},
"correlationId": "Example correlationId",
"error": {}
}{
"success": true,
"message": "Example message",
"code": "NOT_FOUND",
"details": {},
"correlationId": "Example correlationId",
"error": {}
}{
"success": true,
"message": "Example message",
"code": "NOT_FOUND",
"details": {},
"correlationId": "Example correlationId",
"error": {}
}Headers
Signed per-composition write token handed to the widget in widgetic:host as entries.writeToken (and returned by GET /embed/compositions/{compositionId} as entryWriteToken). Required when the request comes from the shared Widgetic artifact origin; verified whenever present.
A random id the widget's browser makes once and keeps (16 to 64 characters of A-Za-z0-9_-), sent so a table's per-visitor limit (declared in the widget database file) can count this browser. Not an identity and not a secret. Without it, or with a malformed value, the limit counts only by network address.
^[A-Za-z0-9_-]{16,64}$Body
Column values to insert (never id or time columns).
Host attribution from the embedding page. It is not authenticated unless the partner signs it (see the description of this operation): never use it for authorization on its own. At most 20 keys; string values up to 500 characters or finite numbers; 2048 bytes JSON total. A signed context carries two more keys, widgetic_exp and widgetic_sig: the gateway checks them and drops them, so they are neither stored nor announced.
Optional per-composition write token (entries.writeToken from widgetic:host, or entryWriteToken from GET /embed/compositions/{compositionId}) when not passed in the X-Widgetic-Write-Token header.