curl --request POST \
--url https://api.blujay.io/v1/vcards \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"first_name": "Northwind",
"last_name": "Support",
"company": "Northwind Supply",
"title": "Customer Support",
"phones": [
{
"number": "+15555550125",
"type": "work"
}
],
"emails": [
{
"address": "support@example.com",
"type": "work"
}
],
"website": "https://example.com/support"
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
first_name: 'Northwind',
last_name: 'Support',
company: 'Northwind Supply',
title: 'Customer Support',
phones: [{number: '+15555550125', type: 'work'}],
emails: [{address: 'support@example.com', type: 'work'}],
website: 'https://example.com/support'
})
};
fetch('https://api.blujay.io/v1/vcards', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.blujay.io/v1/vcards"
payload = {
"first_name": "Northwind",
"last_name": "Support",
"company": "Northwind Supply",
"title": "Customer Support",
"phones": [
{
"number": "+15555550125",
"type": "work"
}
],
"emails": [
{
"address": "support@example.com",
"type": "work"
}
],
"website": "https://example.com/support"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.blujay.io/v1/vcards"
payload := strings.NewReader("{\n \"first_name\": \"Northwind\",\n \"last_name\": \"Support\",\n \"company\": \"Northwind Supply\",\n \"title\": \"Customer Support\",\n \"phones\": [\n {\n \"number\": \"+15555550125\",\n \"type\": \"work\"\n }\n ],\n \"emails\": [\n {\n \"address\": \"support@example.com\",\n \"type\": \"work\"\n }\n ],\n \"website\": \"https://example.com/support\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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))
}require 'uri'
require 'net/http'
url = URI("https://api.blujay.io/v1/vcards")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"first_name\": \"Northwind\",\n \"last_name\": \"Support\",\n \"company\": \"Northwind Supply\",\n \"title\": \"Customer Support\",\n \"phones\": [\n {\n \"number\": \"+15555550125\",\n \"type\": \"work\"\n }\n ],\n \"emails\": [\n {\n \"address\": \"support@example.com\",\n \"type\": \"work\"\n }\n ],\n \"website\": \"https://example.com/support\"\n}"
response = http.request(request)
puts response.read_body<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.blujay.io/v1/vcards",
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([
'first_name' => 'Northwind',
'last_name' => 'Support',
'company' => 'Northwind Supply',
'title' => 'Customer Support',
'phones' => [
[
'number' => '+15555550125',
'type' => 'work'
]
],
'emails' => [
[
'address' => 'support@example.com',
'type' => 'work'
]
],
'website' => 'https://example.com/support'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}{
"status": "success",
"trace_id": "trc_8f2a1c0e4b7d",
"timestamp": "2026-10-14T17:01:00.000Z",
"data": {
"id": "vcard_abc123",
"first_name": "Sam",
"last_name": "Lee",
"company": "Northwind",
"title": "Support Lead",
"phones": [
{
"number": "+15555550100",
"type": "mobile"
}
],
"emails": [
{
"address": "sam@example.com",
"type": "work"
}
],
"website": "https://example.com",
"has_photo": true,
"created_at": "2026-10-14T17:01:00.000Z",
"updated_at": "2026-10-14T17:01:00.000Z"
},
"client_request_id": "<string>"
}{
"status": "error",
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"type": "validation",
"http_status": 400,
"retryable": false,
"doc_url": "https://docs.blujay.io/errors/validation-error",
"details": []
},
"trace_id": "trc_8f2a1c0e4b7d",
"timestamp": "2026-10-14T00:00:00.000Z"
}Save a vCard
Saves a contact card for your workspace, such as your support line or a colleague’s details. To send it, add a vcard part with its ID to a message. Card photos are managed in the Blujay app.
curl --request POST \
--url https://api.blujay.io/v1/vcards \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"first_name": "Northwind",
"last_name": "Support",
"company": "Northwind Supply",
"title": "Customer Support",
"phones": [
{
"number": "+15555550125",
"type": "work"
}
],
"emails": [
{
"address": "support@example.com",
"type": "work"
}
],
"website": "https://example.com/support"
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
first_name: 'Northwind',
last_name: 'Support',
company: 'Northwind Supply',
title: 'Customer Support',
phones: [{number: '+15555550125', type: 'work'}],
emails: [{address: 'support@example.com', type: 'work'}],
website: 'https://example.com/support'
})
};
fetch('https://api.blujay.io/v1/vcards', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.blujay.io/v1/vcards"
payload = {
"first_name": "Northwind",
"last_name": "Support",
"company": "Northwind Supply",
"title": "Customer Support",
"phones": [
{
"number": "+15555550125",
"type": "work"
}
],
"emails": [
{
"address": "support@example.com",
"type": "work"
}
],
"website": "https://example.com/support"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.blujay.io/v1/vcards"
payload := strings.NewReader("{\n \"first_name\": \"Northwind\",\n \"last_name\": \"Support\",\n \"company\": \"Northwind Supply\",\n \"title\": \"Customer Support\",\n \"phones\": [\n {\n \"number\": \"+15555550125\",\n \"type\": \"work\"\n }\n ],\n \"emails\": [\n {\n \"address\": \"support@example.com\",\n \"type\": \"work\"\n }\n ],\n \"website\": \"https://example.com/support\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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))
}require 'uri'
require 'net/http'
url = URI("https://api.blujay.io/v1/vcards")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"first_name\": \"Northwind\",\n \"last_name\": \"Support\",\n \"company\": \"Northwind Supply\",\n \"title\": \"Customer Support\",\n \"phones\": [\n {\n \"number\": \"+15555550125\",\n \"type\": \"work\"\n }\n ],\n \"emails\": [\n {\n \"address\": \"support@example.com\",\n \"type\": \"work\"\n }\n ],\n \"website\": \"https://example.com/support\"\n}"
response = http.request(request)
puts response.read_body<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.blujay.io/v1/vcards",
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([
'first_name' => 'Northwind',
'last_name' => 'Support',
'company' => 'Northwind Supply',
'title' => 'Customer Support',
'phones' => [
[
'number' => '+15555550125',
'type' => 'work'
]
],
'emails' => [
[
'address' => 'support@example.com',
'type' => 'work'
]
],
'website' => 'https://example.com/support'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}{
"status": "success",
"trace_id": "trc_8f2a1c0e4b7d",
"timestamp": "2026-10-14T17:01:00.000Z",
"data": {
"id": "vcard_abc123",
"first_name": "Sam",
"last_name": "Lee",
"company": "Northwind",
"title": "Support Lead",
"phones": [
{
"number": "+15555550100",
"type": "mobile"
}
],
"emails": [
{
"address": "sam@example.com",
"type": "work"
}
],
"website": "https://example.com",
"has_photo": true,
"created_at": "2026-10-14T17:01:00.000Z",
"updated_at": "2026-10-14T17:01:00.000Z"
},
"client_request_id": "<string>"
}{
"status": "error",
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"type": "validation",
"http_status": 400,
"retryable": false,
"doc_url": "https://docs.blujay.io/errors/validation-error",
"details": []
},
"trace_id": "trc_8f2a1c0e4b7d",
"timestamp": "2026-10-14T00:00:00.000Z"
}Authorizations
Your workspace API key, sent as Authorization: Bearer blu_.... Workspace admins can find it in the Blujay app under API.
An API key has full access to your workspace and works on every endpoint. There is no test mode: every key sends real messages and changes real data, so keep it secret and use it only from your server.
Each API key can make 600 read requests and 120 write requests per minute, plus 60 attachment requests per minute. Over a limit you get 429 RATE_LIMIT_EXCEEDED with Retry-After.
Body
Requires at least one phone number or email. Unknown fields are rejected.
1 - 80"Sam"
80"Lee"
120"Northwind"
120"Support Lead"
Phone numbers with the label recipients see. The first is the card's preferred number.
5Show child attributes
Show child attributes
Email addresses with the label recipients see.
5Show child attributes
Show child attributes
An http or https URL.
2048"https://example.com"
Response
The saved vCard.
Wraps every successful response. The result is in data.
success ^trc_[A-Za-z0-9_-]+$"trc_8f2a1c0e4b7d"
"2026-10-14T17:01:00.000Z"
Show child attributes
Show child attributes
Your X-Request-ID header value, echoed back when you send one. Not included on the send responses (POST /v1/chats, POST /v1/chats/{chat_id}/messages, POST /v1/messages/{message_id}/retry); keep your own mapping from your request ID to the returned message ID there.