RustGrid Quickstart

Create your first project, API key, ticket, custom field, and webhook.

These examples use the current OpenAPI contract and `/api/v1` paths. Tenant context comes from the bearer credential you send in `Authorization`, either a tenant-scoped JWT or an `rgk_` API key. Do not send `X-Tenant-Id`; tenant headers are rejected.

Setup

Set variables once.

Use a tenant-scoped JWT for account administration calls. Use the API key returned later for app and automation calls.

Shell variables
export RG_API_BASE="https://app.rustgrid.com"
export JWT_TOKEN="paste_tenant_scoped_jwt_here"
export IDEMPOTENCY_KEY="$(uuidgen)"
Projects

Create your first project.

A project key routes tickets without exposing tenant IDs in client requests.

POST /api/v1/projects
curl -sS -X POST "$RG_API_BASE/api/v1/projects" \
  -H "Authorization: Bearer $JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: project-$IDEMPOTENCY_KEY" \
  -d '{
    "key": "DEMO",
    "name": "Demo Project",
    "description": "Quickstart project for API examples"
  }'

Save the response id as PROJECT_ID for project-scoped endpoints.

Store PROJECT_ID
export PROJECT_ID="paste_project_id_from_response_here"
API keys

Generate an API key.

The full rgk_ key is returned only once. Store it in your secret manager before leaving the response.

POST /api/v1/api-keys
curl -sS -X POST "$RG_API_BASE/api/v1/api-keys" \
  -H "Authorization: Bearer $JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Quickstart automation",
    "project_id": "'$PROJECT_ID'",
    "scopes": [
      "projects:read",
      "tickets:create",
      "tickets:read",
      "tickets:list",
      "custom_fields:create",
      "custom_fields:read",
      "webhooks:create",
      "webhooks:read"
    ]
  }'
Store RG_API_KEY
export RG_API_KEY="paste_rgk_key_from_response_here"
Tickets

Create your first ticket.

Send project_key or project_id. The quickstart uses project_key so the command remains readable.

POST /api/v1/tickets
curl -sS -X POST "$RG_API_BASE/api/v1/tickets" \
  -H "Authorization: Bearer $RG_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: ticket-$IDEMPOTENCY_KEY" \
  -d '{
    "project_key": "DEMO",
    "title": "First RustGrid ticket",
    "description": "Created through the RustGrid quickstart.",
    "type": "task",
    "priority": "medium",
    "status": "todo"
  }'
Tickets

List tickets.

Ticket lists are paginated. The response includes items, page, size, and total.

GET /api/v1/tickets
curl -sS "$RG_API_BASE/api/v1/tickets?page=1&size=20" \
  -H "Authorization: Bearer $RG_API_KEY"
Schema

Add custom fields.

Custom fields are project-level definitions. Supported field types include text, number, date, boolean, single_select, and multi_select.

POST /api/v1/projects/{project_id}/custom-fields
curl -sS -X POST "$RG_API_BASE/api/v1/projects/$PROJECT_ID/custom-fields" \
  -H "Authorization: Bearer $RG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "key": "customer_tier",
    "name": "Customer Tier",
    "description": "Priority segment for support workflows",
    "field_type": "single_select",
    "is_required": false,
    "position": 10,
    "options": [
      { "value": "standard", "label": "Standard", "color": "#0F6FFF" },
      { "value": "priority", "label": "Priority", "color": "#168A5B" }
    ]
  }'
GET /api/v1/projects/{project_id}/custom-fields
curl -sS "$RG_API_BASE/api/v1/projects/$PROJECT_ID/custom-fields?page=1&size=20" \
  -H "Authorization: Bearer $RG_API_KEY"
Webhooks

Create a webhook subscription.

The create response may include a signing secret. Store it once, then use it to verify deliveries.

POST /api/v1/webhooks
curl -sS -X POST "$RG_API_BASE/api/v1/webhooks" \
  -H "Authorization: Bearer $RG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Quickstart webhook",
    "url": "https://example.com/rustgrid/webhook",
    "project_id": "'$PROJECT_ID'",
    "event_types": ["ticket.created", "ticket.updated"]
  }'
Security

Verify webhook signatures.

Verify against the exact raw request body bytes your server received. Do not reserialize parsed JSON before computing the HMAC.

Node.js verification
node - <<'NODE'
const crypto = require('crypto');

function verifyRustGridWebhook(rawBody, signatureHeader, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');

  const received = String(signatureHeader || '').replace(/^sha256=/, '');
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received));
}

const ok = verifyRustGridWebhook(
  Buffer.from(process.env.RUSTGRID_WEBHOOK_BODY || '', 'utf8'),
  process.env.RUSTGRID_WEBHOOK_SIGNATURE,
  process.env.RUSTGRID_WEBHOOK_SECRET
);
process.stdout.write(ok ? 'valid\n' : 'invalid\n');
NODE

Use the signature header name emitted by your RustGrid deployment. Keep webhook secrets out of logs.

Troubleshooting

Common errors and request IDs.

Every response includes or propagates x-request-id. Include that value when debugging failed calls.

StatusTypical causeAction
400Payload shape or enum value does not match the OpenAPI schema.Check required fields, priority, type, status, and custom field types.
401Missing, expired, or invalid bearer credential.Send Authorization: Bearer ...; do not send X-Tenant-Id.
403The credential lacks the needed scope or RBAC permission.Create a key with the scopes used by the endpoint.
404The resource is absent or outside the caller tenant.Confirm IDs came from the same bearer credential context.
409Duplicate resource or idempotency conflict.Reuse the same body for idempotency replay or send a new key.
429Route, tenant, or plan rate limit reached.Back off using retry-after and inspect x-ratelimit-* headers.
Capture status and x-request-id
curl -sS -D /tmp/rustgrid-headers.txt "$RG_API_BASE/api/v1/tickets?page=1&size=1" \
  -H "Authorization: Bearer $RG_API_KEY" \
  -o /tmp/rustgrid-body.json

printf "status/request headers:\n"
grep -iE '^(http/|x-request-id:|retry-after:|x-ratelimit-)' /tmp/rustgrid-headers.txt
cat /tmp/rustgrid-body.json