Skip to main content
POST
/
v2
/
accounts
/
{account_id}
/
sub-accounts
/
{sub_account_id}
/
api-keys
AhaSend Go SDK
package main

import (
  "context"
  "fmt"
  "log"

  "github.com/AhaSend/ahasend-go/api"
  "github.com/AhaSend/ahasend-go/models/requests"
  "github.com/google/uuid"
)

func main() {
  // Create API client with authentication
  client := api.NewAPIClient(
    api.WithAPIKey("aha-sk-your-64-character-key"),
  )

  accountID := uuid.New()
  subAccountID := uuid.New()

  // Create context for the API call
  ctx := context.Background()

  // Bootstrap an API key for the sub-account (idempotency key makes retries safe)
  response, httpResp, err := client.SubAccountsAPI.CreateSubAccountAPIKey(
    ctx,
    accountID,
    subAccountID,
    requests.CreateAPIKeyRequest{
      Label:  "Bootstrap key",
      Scopes: []string{"messages:send:all", "domains:read"},
      // Optional: restrict the sub-account key to specific source IPs.
      IPAllowList: []string{"203.0.113.0/24"},
    },
    api.WithIdempotencyKey("child-bootstrap-key-20240101-acme"),
  )
  if err != nil {
    log.Fatalf("Error creating sub-account API key: %v", err)
  }

  if httpResp.StatusCode == 201 {
    fmt.Printf("✅ Sub-account API key created. Status: %d\n", httpResp.StatusCode)
    fmt.Printf("ID: %s\n", response.ID)
    fmt.Printf("Label: %s\n", response.Label)
    fmt.Printf("Public key: %s\n", response.PublicKey)
    // SecretKey is the one-time secret, returned ONLY on create.
    if response.SecretKey != nil {
      // Store this value immediately — it cannot be retrieved again later.
      fmt.Printf("Secret key (store this now, shown only once): %s\n", *response.SecretKey)
    }
  } else {
    fmt.Printf("❌ Unexpected status code: %d\n", httpResp.StatusCode)
  }
}
{
  "object": "api_key",
  "id": "13b3aa8e-78d3-48a1-92d2-4b8b1228c2dd",
  "created_at": "2024-01-01T00:05:00Z",
  "updated_at": "2024-01-01T00:05:00Z",
  "last_used_at": null,
  "account_id": "2f3c5d2a-9ef8-4c91-a5f4-79990c8c1d3a",
  "label": "Bootstrap key",
  "public_key": "aha-pk-child-public-key",
  "secret_key": "aha-sk-child-secret-key",
  "scopes": [
    {
      "id": "c574470d-76ef-4f74-9b24-70a583a17e03",
      "created_at": "2024-01-01T00:05:00Z",
      "updated_at": "2024-01-01T00:05:00Z",
      "api_key_id": "13b3aa8e-78d3-48a1-92d2-4b8b1228c2dd",
      "scope": "messages:send:all",
      "domain_id": null
    }
  ],
  "ip_allow_list": []
}
Platform Partner feature: Sub Accounts is part of our Platform Partner capabilities and is currently in early access. Contact us to enable it on your account.

Authorizations

Authorization
string
header
required

API key for authentication

Headers

Idempotency-Key
string

Optional idempotency key for safe request retries. Must be a unique string for each logical request. Requests with the same key will return the same response. Keys for non-secret responses expire after 24 hours. API-key create responses include a one-time secret_key, so their encrypted replay responses expire after 5 minutes.

Maximum string length: 255

Path Parameters

account_id
string<uuid>
required

Parent account ID

sub_account_id
string<uuid>
required

Sub account ID

Body

application/json
label
string
required

Human-readable label for the API key; must not be empty

Required string length: 1 - 255
scopes
string[]
required

Array of scope strings to grant to this API key

Minimum array length: 1
ip_allow_list
string[]

Optional 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

Sub-account API key created successfully

object
enum<string>
required

Object type identifier

Available options:
api_key
id
string<uuid>
required

Unique identifier for the API key

created_at
string<date-time>
required

When the API key was created

updated_at
string<date-time>
required

When the API key was last updated

account_id
string<uuid>
required

Account ID this API key belongs to

label
string
required

Human-readable label for the API key

public_key
string
required

Public portion of the API key

scopes
object[]
required

Scopes granted to this API key

ip_allow_list
string[]
required

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.

last_used_at
string<date-time> | null

When the API key was last used (updates every 5-10 minutes)

secret_key
string

Secret key. Only returned when an API key is created, including exact idempotent replays of create requests within the 5-minute secret-bearing replay window. Store it immediately; list, get, update, and delete responses omit it.