Quickstart
Call any service without API keys. No setup. Zero credentials.
Call a service in one line
import { call } from "@private.me/xbind";
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/xbind
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: 'xbind',
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/xbind";
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/xbind";
const api = new DualModeAdapter({
xbind: 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.
Framework Integration Quickstarts
Get started with identity-based authentication for your AI framework in 60 seconds:
Available Framework Integrations
Each quickstart includes installation, authentication setup, and example code. View complete white papers for pricing and purchase API documentation.
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.