Skip to main content
POST
/
api
/
line-requests
Create Line Request
curl --request POST \
  --url https://app.tuco.ai/api/line-requests \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "firstName": "<string>",
  "lastName": "<string>",
  "channelType": "<string>",
  "lineType": "<string>",
  "email": "<string>",
  "phone": "<string>",
  "zipcode": "<string>",
  "state": "<string>",
  "forwardCallsTo": "<string>",
  "profileImageUrl": "<string>",
  "idempotencyKey": "<string>"
}
'
Use this endpoint after the workspace already has an active Tuco subscription. If the request is included in the current plan, Tuco starts provisioning immediately. If the request requires an add-on, Tuco returns a hosted invoice link.

What This Endpoint Decides

You send the line details once, and Tuco decides what should happen next. The response will always fall into one of these buckets:
  • provisioning Tuco accepted the request immediately and has started setting up the line.
  • awaiting_payment Tuco saved the request, but it needs payment before provisioning can begin.
  • subscription_required The workspace needs an active base plan before it can request lines.
  • forbidden_line_type The current plan does not allow that request, or the plan reached a hard cap with no paid expansion path.

Authentication

Pass your workspace API key as a Bearer token, or use a Clerk session token.
Authorization: Bearer tuco_xxxxxxxxxxxxx

Request body

firstName
string
Required. Display first name for the line.
lastName
string
Required. Display last name for the line.
channelType
string
Required line channel. Supported values: "phone" or "email".
lineType
string
Optional for phone lines. Supported values: "purchased" or "byon". Defaults to "purchased" when omitted.
email
string
Required for email lines. Also required for BYON phone lines.
phone
string
Required for BYON phone lines.
zipcode
string
Optional. Used for purchased phone provisioning when relevant.
state
string
Optional. Used for purchased phone provisioning when relevant.
forwardCallsTo
string
Optional. Forwarding destination for phone lines.
profileImageUrl
string
Optional. Avatar shown in Tuco.
idempotencyKey
string
Optional. You can also send this as the Idempotency-Key header. Reusing the same key returns the original response instead of creating a duplicate request.

Required field rules

Request typeRequired fields
Email linefirstName, lastName, channelType=email, email
Purchased phone linefirstName, lastName, channelType=phone
BYON phone linefirstName, lastName, channelType=phone, lineType=byon, email, phone
Extra fields are tolerated. Tuco validates the fields required for the declared request type.

Default behavior for phone lines

If channelType is phone and lineType is omitted, Tuco treats the request as:
{ "lineType": "purchased" }
That means BYON validation only applies when you explicitly send lineType: "byon".

Example: included line

curl -X POST "https://app.tuco.ai/api/line-requests" \
  -H "Authorization: Bearer tuco_xxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: line-req-001" \
  -d '{
    "firstName": "Sales",
    "lastName": "Line 1",
    "channelType": "phone",
    "lineType": "purchased"
  }'

Success: provisioning (201 Created)

{
  "success": true,
  "status": "provisioning",
  "paymentRequired": false,
  "line": {
    "_id": "6a091e74542d9e3d645925ae",
    "firstName": "Sales",
    "lastName": "Line 1",
    "channelType": "phone",
    "lineType": "purchased",
    "provisioningStatus": "provisioning",
    "paymentStatus": "not_required"
  }
}

Example: paid add-on line

curl -X POST "https://app.tuco.ai/api/line-requests" \
  -H "Authorization: Bearer tuco_xxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: line-req-002" \
  -d '{
    "firstName": "Support",
    "lastName": "Email 2",
    "channelType": "email",
    "email": "support2@example.com"
  }'

Success: awaiting payment (201 Created)

{
  "success": true,
  "status": "awaiting_payment",
  "paymentRequired": true,
  "paymentReason": "over_plan_limit",
  "paymentUrl": "https://invoice.stripe.com/...",
  "expiresAt": null,
  "line": {
    "_id": "6a09279daa903300e2530b0f",
    "firstName": "Support",
    "lastName": "Email 2",
    "channelType": "email",
    "lineType": "purchased",
    "provisioningStatus": "awaiting_payment",
    "paymentStatus": "awaiting_payment",
    "stripeInvoiceId": "in_123",
    "paymentLinkUrl": "https://invoice.stripe.com/..."
  }
}

What To Do After You Create A Request

If the response is provisioning:
  • keep the returned line._id
  • poll GET /api/line-requests/:id for status updates
If the response is awaiting_payment:
  • keep the returned line._id
  • show the paymentUrl to the user
  • after payment, poll GET /api/line-requests/:id until it changes to provisioning
You should not submit the same line details again after payment. The original request is the one that continues.

One Request At A Time

Tuco blocks a new request only when the workspace already has a line request in awaiting_payment. This prevents:
  • duplicate unpaid requests
  • accidental double purchases
  • race conditions from retries
Lines already in provisioning do not block the next request.

Error responses

StatusWhenBody
400Missing required fields or invalid types{ "error": "..." }
401Missing or invalid API key / session{ "error": "Unauthorized" }
403Workspace has no active subscription{ "success": false, "status": "subscription_required", "message": "...", "redirect": "/billing" }
403Line type is not allowed on the plan, or hard cap reached with no paid expansion{ "success": false, "status": "forbidden_line_type", "message": "..." }
409Another line request is already waiting for payment{ "success": false, "status": "request_in_progress", "message": "...", "existingLine": { ... } }
429Rate limit exceeded{ "error": "Rate limit exceeded", "retryAfterMs": 1234 }

Notes

  • Payment is always attached to the exact saved line request. The caller does not need to resubmit the payload after paying.
  • The API uses the authenticated workspace. You cannot request lines for an arbitrary workspace id in the body.
  • Retry safety is built in through idempotency support and workspace-level request locking.