Step 1
Set up Relay
- Create and save a form in Relay.Add the contact fields and custom fields your website will send.
- Copy the form ID.On the Forms page, find the form card and select
Copybeside its Form ID. - Generate a company API key.Go to Settings → Integrations → Website Form API. Copy the key when Relay shows it.
- Add the key to your website server.Save it as a private environment variable named
RELAY_API_KEY. Save the form ID asRELAY_FORM_ID.
Step 2
Authenticate the request
Send the company API key in the Authorization header on every request.
Authorization: Bearer YOUR_RELAY_API_KEYCall Relay from your website's server. Never put this key in HTML, browser JavaScript, a React client component, or a public environment variable such as NEXT_PUBLIC_RELAY_API_KEY.
Step 3
Send the form data
Send JSON using only the fields included on the selected Relay form. Every form must receive a fullName or company. Any field marked required in Relay must also be included.
{
"company": "Example Restaurant",
"email": "owner@example.com"
}| Field | Type | What to send |
|---|---|---|
fullName | string | A person's full name. |
company | string | A company, organization, or restaurant name. |
email | string | A valid email address. |
phone | string | A phone number with 7 to 15 digits. |
jobTitle | string | The person's job title. |
website | string | A valid website URL. |
fields | object | Optional custom form values keyed by custom field ID. |
Custom fields
Put custom field values inside fields. Each key must be the ID of a custom field on that Relay form, and every value must be a string.
{
"company": "Example Restaurant",
"email": "owner@example.com",
"fields": {
"CUSTOM_FIELD_ID": "Dinner"
}
}Step 4
Choose your language
These examples send a company name and email address. Replace the sample values with data from your website form.
cURL
Available in most terminals. Replace the three placeholder values before running it.
curl --request POST \
"https://www.tapintorelay.com/api/v1/forms/FORM_ID/submissions" \
--header "Authorization: Bearer YOUR_RELAY_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"company": "Example Restaurant",
"email": "owner@example.com"
}'JavaScript / Node.js
Uses the built-in fetch function in Node.js 18 or newer. No package is required.
const formId = process.env.RELAY_FORM_ID;
const apiKey = process.env.RELAY_API_KEY;
const response = await fetch(
`https://www.tapintorelay.com/api/v1/forms/${formId}/submissions`,
{
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
company: "Example Restaurant",
email: "owner@example.com",
}),
},
);
const result = await response.json();
if (!response.ok) {
throw new Error(result.message ?? "Relay submission failed");
}
console.log(result);Python
Install Requests first with: pip install requests
import os
import requests
form_id = os.environ["RELAY_FORM_ID"]
api_key = os.environ["RELAY_API_KEY"]
url = f"https://www.tapintorelay.com/api/v1/forms/{form_id}/submissions"
response = requests.post(
url,
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
},
json={
"company": "Example Restaurant",
"email": "owner@example.com",
},
timeout=15,
)
response.raise_for_status()
print(response.json())PHP
Uses PHP's cURL extension. Store the values as private server environment variables.
<?php
$formId = getenv('RELAY_FORM_ID');
$apiKey = getenv('RELAY_API_KEY');
$url = "https://www.tapintorelay.com/api/v1/forms/{$formId}/submissions";
$request = curl_init($url);
curl_setopt_array($request, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer {$apiKey}",
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"company" => "Example Restaurant",
"email" => "owner@example.com",
]),
]);
$body = curl_exec($request);
$status = curl_getinfo($request, CURLINFO_HTTP_CODE);
curl_close($request);
if ($status < 200 || $status >= 300) {
throw new RuntimeException("Relay submission failed: {$body}");
}
echo $body;Ruby
Uses Ruby's built-in Net::HTTP library. No gem is required.
require "json"
require "net/http"
require "uri"
form_id = ENV.fetch("RELAY_FORM_ID")
api_key = ENV.fetch("RELAY_API_KEY")
uri = URI("https://www.tapintorelay.com/api/v1/forms/#{form_id}/submissions")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer #{api_key}"
request["Content-Type"] = "application/json"
request.body = {
company: "Example Restaurant",
email: "owner@example.com"
}.to_json
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
raise "Relay submission failed: #{response.body}" unless response.is_a?(Net::HTTPSuccess)
puts response.bodyResult
Successful response
Relay returns HTTP 201 Created when the submission is accepted.
{
"success": true,
"submissionId": "SUBMISSION_ID",
"contactId": "CONTACT_ID",
"existingContact": false
}When existingContact is true, Relay matched the submission to a contact already in the workspace by email or phone.
Reference
Errors and limits
| Status | Error | Meaning |
|---|---|---|
400 | invalid_json / invalid_request | The JSON or request shape is invalid. |
401 | invalid_api_key | The API key is missing, invalid, or revoked. |
403 | feature_not_available | Website Form API access requires Relay Pro. |
404 | form_not_found | The form ID does not belong to this Relay company. |
409 | capacity_reached | The workspace has reached its contact limit. |
413 | payload_too_large | The JSON body is larger than 32 KB. |
422 | invalid_submission | A required or configured form value is invalid. |
429 | rate_limited | Too many submissions were sent. Retry after 60 seconds. |
Each API key and form combination can accept up to 120 requests per minute and 10,000 requests per day. Request bodies are limited to 32 KB.
Ready to connect a form?
