private.me

Quickstart

Call any service without API keys. No setup. Zero credentials.

Call a service in one line

import { call } from "@private.me/xlink";

const result = await call("payments:createCharge", {
  amount: 100,
  currency: "USD"
});

That's it. No API keys. No tokens. No setup steps.

Install

npm install @private.me/xlink

Purchase & Deploy an ACI

Purchase any ACI programmatically using the Private.Me purchase API. All purchases support idempotency keys to prevent duplicate charges and include structured error handling.

Basic Purchase (TypeScript)

import { randomUUID } from 'crypto';

const response = await fetch('https://private.me/api/purchase', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Idempotency-Key': randomUUID(),
    'X-Client-Type': 'ai-agent' // Optional: 60 req/min vs 10 req/min
  },
  body: JSON.stringify({
    product: 'xlink',
    tier: 'basic', // 'basic' | 'middle' | 'enterprise'
    connection_id: 'my-production-connection'
  })
});

const data = await response.json();

if (response.ok) {
  console.log('Purchase successful:', data.subscription_id);
} else {
  // Parse RFC 7807 error
  console.error(data.title);
  if (data.fields) {
    // Field-level validation errors
    Object.entries(data.fields).forEach(([field, error]) => {
      console.error(`${field}: ${error}`);
    });
  }
}

Retry Logic for Rate Limits (TypeScript)

async function purchaseWithRetry(params, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    const response = await fetch('https://private.me/api/purchase', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Idempotency-Key': randomUUID(),
        'X-Client-Type': 'ai-agent'
      },
      body: JSON.stringify(params)
    });

    if (response.status === 429) {
      const retryAfter = parseInt(response.headers.get('Retry-After') || '60');
      console.log(`Rate limited. Retrying in ${retryAfter}s...`);
      await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
      continue;
    }

    return response.json();
  }
  throw new Error('Max retries exceeded');
}

Python Purchase with Error Handling

import requests
import uuid
import time

def purchase_aci(product: str, tier: str, connection_id: str, is_ai_agent: bool = False):
    headers = {
        'Content-Type': 'application/json',
        'Idempotency-Key': str(uuid.uuid4())
    }

    if is_ai_agent:
        headers['X-Client-Type'] = 'ai-agent'  # 60 req/min

    response = requests.post('https://private.me/api/purchase',
        headers=headers,
        json={
            'product': product,
            'tier': tier,
            'connection_id': connection_id
        }
    )

    if response.status_code == 429:
        retry_after = int(response.headers.get('Retry-After', 60))
        print(f'Rate limited. Waiting {retry_after}s...')
        time.sleep(retry_after)
        return purchase_aci(product, tier, connection_id, is_ai_agent)

    data = response.json()

    if response.status_code == 200:
        return {
            'success': True,
            'subscription_id': data['subscription_id'],
            'connection_id': data['connection_id']
        }
    else:
        return {
            'success': False,
            'error': data.get('title', 'Unknown error'),
            'fields': data.get('fields', {})
        }

Go Purchase with Structured Errors

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
    "time"
    "github.com/google/uuid"
)

type PurchaseRequest struct {
    Product      string `json:"product"`
    Tier         string `json:"tier"`
    ConnectionID string `json:"connection_id"`
}

type ErrorResponse struct {
    Type   string            `json:"type"`
    Title  string            `json:"title"`
    Detail string            `json:"detail"`
    Fields map[string]string `json:"fields,omitempty"`
}

func purchaseACI(req PurchaseRequest, isAIAgent bool) error {
    body, _ := json.Marshal(req)

    httpReq, _ := http.NewRequest("POST", "https://private.me/api/purchase", bytes.NewReader(body))
    httpReq.Header.Set("Content-Type", "application/json")
    httpReq.Header.Set("Idempotency-Key", uuid.New().String())

    if isAIAgent {
        httpReq.Header.Set("X-Client-Type", "ai-agent")
    }

    resp, err := http.DefaultClient.Do(httpReq)
    if err != nil {
        return err
    }
    defer resp.Body.Close()

    if resp.StatusCode == 429 {
        retryAfter := resp.Header.Get("Retry-After")
        if retryAfter == "" {
            retryAfter = "60"
        }
        duration, _ := time.ParseDuration(retryAfter + "s")
        time.Sleep(duration)
        return purchaseACI(req, isAIAgent)
    }

    if resp.StatusCode != 200 {
        var errResp ErrorResponse
        json.NewDecoder(resp.Body).Decode(&errResp)

        if len(errResp.Fields) > 0 {
            for field, msg := range errResp.Fields {
                fmt.Printf("Field %s: %s\n", field, msg)
            }
        }
        return fmt.Errorf("%s: %s", errResp.Title, errResp.Detail)
    }

    return nil
}

Idempotency: The Idempotency-Key header prevents duplicate purchases if the same request is sent multiple times. Use a unique UUID for each purchase attempt.

Rate Limits: AI agents get 60 requests/minute with X-Client-Type: ai-agent header. Human-initiated requests are limited to 10 requests/minute. The API returns 429 Too Many Requests with a Retry-After header when limits are exceeded.

Error Handling: All errors follow RFC 7807 Problem Details format with type, title, detail, and optional fields object for field-level validation errors.

What you just did (optional)

You connected to the payments service, authenticated both sides automatically, and sent a signed request.

No credentials were created or stored.

Handle the response

const result = await call("crm:searchContacts", { q: "Alice" });

if (result.ok) {
  console.log("Found:", result.value);
} else {
  console.error(result.error.code, result.error.message);

  // Field-level validation errors (if present)
  if (result.error.fields) {
    Object.entries(result.error.fields).forEach(([field, error]) => {
      console.error(`  ${field}: ${error}`);
    });
  }
}

The Result pattern gives you safe, structured error handling with field-level validation details when applicable.

Or use unwrap for simple cases

// Throws on error, returns value directly
const contacts = await call("crm:searchContacts", {
  q: "Alice"
}).unwrap();

If you need more control

Reuse a connection

import { connect } from "@private.me/xlink";

const payments = await connect("payments");

await payments.send({ action: "createCharge", amount: 100 });
await payments.send({ action: "refund", amount: 50 });

Persistent connections reduce handshake overhead for multiple calls.

Connect to your own service

Use an invite (one-time approval), then call it the same way:

await call("billing:createInvoice", { amount: 99.5 });

That's the whole model

Call services by name. No API keys. Works like a normal request.

How it works (optional)

Expand to learn more

Each service has a cryptographic identity. Requests are signed per-call (no reusable tokens). Both sides verify each other.

No long-lived credentials means nothing to rotate or leak.

Upgrade security (optional)

await call("vault:getPII", { id: "123" }, { mode: "secure" });

simple (default): Fastest, replaces API keys
secure: Multi-path with stronger guarantees

Migrate from existing APIs

import { DualModeAdapter } from "@private.me/xlink";

const api = new DualModeAdapter({
  xlink: true,
  fallback: {
    url: "https://api.example.com",
    apiKey: process.env.API_KEY
  }
});

await api.call("createCharge", { amount: 100 });

Tries Private.Me first. Falls back automatically. Remove API keys when ready.

Done

You now have keyless authentication, encrypted requests, and zero-setup connections.

Next: See Concepts to understand how it works, or White Papers to explore the 207 ACIs available.