package main
import (
"context"
"fmt"
"log"
"os"
"github.com/AhaSend/ahasend-go/api"
"github.com/AhaSend/ahasend-go/models/requests"
"github.com/google/uuid"
)
func main() {
client := api.NewAPIClient(api.WithAPIKey(os.Getenv("AHASEND_API_KEY")))
accountID := uuid.MustParse(os.Getenv("AHASEND_ACCOUNT_ID"))
ctx := context.Background()
// The SDK sends a fresh Idempotency-Key with this POST and reuses it on
// its own retries, so a retried request is not applied twice.
response, _, err := client.APIKeysAPI.CreateAPIKey(ctx, accountID, requests.CreateAPIKeyRequest{
Label: "My API Key",
Scopes: []string{
"messages:read:all",
"domains:read",
},
// Optional: restrict this key to specific source IPs (CIDR blocks or
// bare IPv4/IPv6 addresses). Omit it to allow any IP.
IPAllowList: []string{"203.0.113.0/24", "198.51.100.7"},
})
if err != nil {
log.Fatalf("Error creating API key: %v", err)
}
// response.SecretKey is returned only on create: put it in your secret
// store now, and never log it.
fmt.Printf("Created API key %s (%s)\n", response.ID, response.Label)
}import { AhaSendClient } from "@ahasend/sdk";
const client = AhaSendClient.fromEnv();
// The SDK sends a fresh Idempotency-Key automatically and reuses it on its own retries.
const apiKey = await client.apiKeys.create({
label: "Production API key",
scopes: ["messages:send:all"],
});
console.log("API key created.", { id: apiKey.id, label: apiKey.label });
curl --request POST \
--url https://api.ahasend.com/v2/accounts/{account_id}/api-keys \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"label": "Production API Key",
"scopes": [
"messages:send:all",
"domains:read"
],
"ip_allow_list": [
"203.0.113.0/24",
"198.51.100.7"
]
}
'import requests
url = "https://api.ahasend.com/v2/accounts/{account_id}/api-keys"
payload = {
"label": "Production API Key",
"scopes": ["messages:send:all", "domains:read"],
"ip_allow_list": ["203.0.113.0/24", "198.51.100.7"]
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.ahasend.com/v2/accounts/{account_id}/api-keys",
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([
'label' => 'Production API Key',
'scopes' => [
'messages:send:all',
'domains:read'
],
'ip_allow_list' => [
'203.0.113.0/24',
'198.51.100.7'
]
]),
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;
}HttpResponse<String> response = Unirest.post("https://api.ahasend.com/v2/accounts/{account_id}/api-keys")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"label\": \"Production API Key\",\n \"scopes\": [\n \"messages:send:all\",\n \"domains:read\"\n ],\n \"ip_allow_list\": [\n \"203.0.113.0/24\",\n \"198.51.100.7\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.ahasend.com/v2/accounts/{account_id}/api-keys")
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 \"label\": \"Production API Key\",\n \"scopes\": [\n \"messages:send:all\",\n \"domains:read\"\n ],\n \"ip_allow_list\": [\n \"203.0.113.0/24\",\n \"198.51.100.7\"\n ]\n}"
response = http.request(request)
puts response.read_body{
"object": "api_key",
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"last_used_at": "2023-11-07T05:31:56Z",
"account_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"label": "<string>",
"public_key": "<string>",
"scopes": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"api_key_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"scope": "<string>",
"domain_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
],
"ip_allow_list": [
"<string>"
],
"secret_key": "<string>"
}{
"message": "Error message"
}{
"message": "Error message"
}{
"message": "Error message"
}{
"message": "A request with this idempotency key is already in progress"
}{
"message": "idempotency key was already used with a different request payload"
}{
"message": "Error message"
}Create an API Key via the API
Create a scoped AhaSend API key through the API, with an optional IP allow list. The secret_key is returned only once, so store it right away.
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/AhaSend/ahasend-go/api"
"github.com/AhaSend/ahasend-go/models/requests"
"github.com/google/uuid"
)
func main() {
client := api.NewAPIClient(api.WithAPIKey(os.Getenv("AHASEND_API_KEY")))
accountID := uuid.MustParse(os.Getenv("AHASEND_ACCOUNT_ID"))
ctx := context.Background()
// The SDK sends a fresh Idempotency-Key with this POST and reuses it on
// its own retries, so a retried request is not applied twice.
response, _, err := client.APIKeysAPI.CreateAPIKey(ctx, accountID, requests.CreateAPIKeyRequest{
Label: "My API Key",
Scopes: []string{
"messages:read:all",
"domains:read",
},
// Optional: restrict this key to specific source IPs (CIDR blocks or
// bare IPv4/IPv6 addresses). Omit it to allow any IP.
IPAllowList: []string{"203.0.113.0/24", "198.51.100.7"},
})
if err != nil {
log.Fatalf("Error creating API key: %v", err)
}
// response.SecretKey is returned only on create: put it in your secret
// store now, and never log it.
fmt.Printf("Created API key %s (%s)\n", response.ID, response.Label)
}import { AhaSendClient } from "@ahasend/sdk";
const client = AhaSendClient.fromEnv();
// The SDK sends a fresh Idempotency-Key automatically and reuses it on its own retries.
const apiKey = await client.apiKeys.create({
label: "Production API key",
scopes: ["messages:send:all"],
});
console.log("API key created.", { id: apiKey.id, label: apiKey.label });
curl --request POST \
--url https://api.ahasend.com/v2/accounts/{account_id}/api-keys \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"label": "Production API Key",
"scopes": [
"messages:send:all",
"domains:read"
],
"ip_allow_list": [
"203.0.113.0/24",
"198.51.100.7"
]
}
'import requests
url = "https://api.ahasend.com/v2/accounts/{account_id}/api-keys"
payload = {
"label": "Production API Key",
"scopes": ["messages:send:all", "domains:read"],
"ip_allow_list": ["203.0.113.0/24", "198.51.100.7"]
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.ahasend.com/v2/accounts/{account_id}/api-keys",
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([
'label' => 'Production API Key',
'scopes' => [
'messages:send:all',
'domains:read'
],
'ip_allow_list' => [
'203.0.113.0/24',
'198.51.100.7'
]
]),
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;
}HttpResponse<String> response = Unirest.post("https://api.ahasend.com/v2/accounts/{account_id}/api-keys")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"label\": \"Production API Key\",\n \"scopes\": [\n \"messages:send:all\",\n \"domains:read\"\n ],\n \"ip_allow_list\": [\n \"203.0.113.0/24\",\n \"198.51.100.7\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.ahasend.com/v2/accounts/{account_id}/api-keys")
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 \"label\": \"Production API Key\",\n \"scopes\": [\n \"messages:send:all\",\n \"domains:read\"\n ],\n \"ip_allow_list\": [\n \"203.0.113.0/24\",\n \"198.51.100.7\"\n ]\n}"
response = http.request(request)
puts response.read_body{
"object": "api_key",
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"last_used_at": "2023-11-07T05:31:56Z",
"account_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"label": "<string>",
"public_key": "<string>",
"scopes": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"api_key_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"scope": "<string>",
"domain_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
],
"ip_allow_list": [
"<string>"
],
"secret_key": "<string>"
}{
"message": "Error message"
}{
"message": "Error message"
}{
"message": "Error message"
}{
"message": "A request with this idempotency key is already in progress"
}{
"message": "idempotency key was already used with a different request payload"
}{
"message": "Error message"
}Related Guides
- API authentication, Permission scopes, Scoped credentials.
- API key CLI, IP allow lists, API errors.
- API rate limits.
Authorizations
API key for authentication. Non-empty Security Requirement values are AhaSend API-key roles. Roles listed within one requirement object are jointly required; separate requirement objects are alternatives.
Headers
Optional idempotency key for safe request retries. Must be a unique string for each logical request.
An identical request with a completed stored outcome returns the original status and body. An in-progress
execution returns 409, a changed method/path/body returns 422, and a released 5xx execution may run again.
Keys for non-secret responses expire after 24 hours. API-key create responses include a one-time secret_key,
so successful encrypted replay responses for those operations expire after 5 minutes.
255Path Parameters
Account ID
Body
Human-readable label for the API key; must not be empty
1 - 255Array of scope strings to grant to this API key
1Optional list of source IPs allowed to authenticate with this key. Each entry is a CIDR block (e.g. 203.0.113.0/24) or a bare IPv4/IPv6 address (stored as a /32 or /128). Entries are canonicalized (host bits are masked) and de-duplicated. The allow-all prefixes 0.0.0.0/0 and ::/0 are rejected, and at most 100 entries are allowed after de-duplication. Omit the field or pass an empty array to leave the key usable from any IP.
Response
API key created successfully
Object type identifier
api_key Unique identifier for the API key
When the API key was created
When the API key was last updated
When the API key was last used (updates every 5-10 minutes)
Account ID this API key belongs to
Human-readable label for the API key
Public portion of the API key
Scopes granted to this API key
Show child attributes
Show child attributes
Source IPs allowed to authenticate with this API key, as canonical CIDR blocks (a bare address is stored as a /32 for IPv4 or /128 for IPv6). Always present; an empty array means the key may be used from any source IP. When non-empty, an authenticated request whose client IP is not covered by an entry is rejected with HTTP 403 on every v2 endpoint, regardless of the key's scopes.
One-time secret key. Store it immediately. Exact successful idempotent replays return the same secret during the 5-minute encrypted replay window.

