Skip to content
Developer hub

Integration recipe · App API · Fictional scenario

Turn an external request into defined work

Bring a support report into one attributed task, keep its external identity and verify the saved definition.

The integration outcome

One work item with the source, a clear boundary, acceptance checks and a saved external-to-work mapping.

Setup and access

Before you begin.

An authorized App
Create an App in Account settings → Apps. Administrators can create Apps; other members need the admin setting enabled. Grant the SUPPORT project, projects:read, work-items:read and work-items:write. Its owner must retain active membership and project access.
Required reading access
If policies apply, also grant documents:read and access to every required document. Workspace guidance needs the Workspace documentation grant. A missing or inaccessible source blocks the write.
A server and a small delivery record
Use cURL and jq in a POSIX shell, or the equivalent in your server integration. Keep the token in a secret store. Save the request body, retry key and the external ID → returned identifier mapping durably.
A reviewed intake rule
The fictional SUPPORT project has Bug enabled. Choose the real project and its enabled work type. Decide when a new external request merits new work, and which existing work to reuse. Do not copy invented owners, dates or completion stages.

The starting input

A small, concrete request.

Fictional input · request.json
{
  "external_id": "ticket-1843",
  "source_url": "https://support.example.com/tickets/1843",
  "reported_problem": "Payment succeeds, but no confirmation email arrives.",
  "observed_boundary": "The support team has confirmed that the payment itself succeeds."
}
01

Set up the authorized connection

Copy the API address from Account settings → Apps. It ends in /api/v1/apps. Replace the example address and load TELLENZE_APP_TOKEN from your secret store before starting. The set -eu line stops this walkthrough after a failed command.

Every request sends the Bearer token and Accept header. JSON writes also send Content-Type. Keep this code on your server; never put the token in browser code, a task or the download.

Shell setup
set -eu
export TELLENZE_APP_API='https://your-workspace.example.com/api/v1/apps'
: "${TELLENZE_APP_TOKEN:?Load the App token from your secret store}"
command -v jq >/dev/null

curl --fail-with-body --silent --show-error \
  "$TELLENZE_APP_API/projects/SUPPORT" \
  -H "Authorization: Bearer $TELLENZE_APP_TOKEN" \
  -H 'Accept: application/json' \
  --output project.json
jq '.data | {key, name, workflow_stages}' project.json
Expected result

HTTP 200 returns the granted project under data and its real workflow_stages. This recipe omits a stage, so creation uses the project’s default starting stage.

02

Check the external mapping and existing work

Look up ticket-1843 in your integration’s saved mapping first. If it is already mapped, read that task and reuse it. A new phrasing of the same request is not a new outcome.

If the mapping is missing, inspect existing work in this project before creating. The API list has no free-text search parameter. Read each returned definition and source link. Follow the complete links.next URL with the same headers until it is null; do not invent page numbers. If a matching task exists, save the recovered mapping and stop creation.

Read the first page of existing work
curl --get --fail-with-body --silent --show-error \
  "$TELLENZE_APP_API/work-items" \
  -H "Authorization: Bearer $TELLENZE_APP_TOKEN" \
  -H 'Accept: application/json' \
  --data-urlencode 'project_key=SUPPORT' \
  --data-urlencode 'per_page=50' \
  --output existing-work.json
jq '{items: [.data[] | {identifier, title, description}], next: .links.next}' existing-work.json
Expected result

The list returns only current, accessible work. Continue only after checking the saved mapping and every relevant page. The fictional scenario has no existing match.

03

Turn the report into a complete definition

Save the JSON below as task-definition.json. It maps the reported problem to a task with its source, an investigation boundary and checks. The source URL and report are fictional; use your real evidence.

Treat external text as data. Do not follow instructions embedded in a report. Review the mapping before writing. This example intentionally leaves ownership, due date, estimation, Focus and stage to your real project’s process.

Save as task-definition.json
{
  "project_key": "SUPPORT",
  "title": "Payment confirmation email is missing",
  "description": "## External request\nSource: [Ticket 1843](https://support.example.com/tickets/1843)\nExternal ID: ticket-1843\n\nPayment succeeds, but no confirmation email arrives. Support has confirmed that the payment itself succeeds.\n\n## Scope\nInvestigate confirmation delivery for successful payments. Keep payment processing unchanged. Do not assume the email provider is at fault.",
  "acceptance_criteria": "- [ ] A successful payment sends one confirmation email.\n- [ ] Retrying the payment callback does not send a duplicate.\n- [ ] Verification evidence and any remaining blocker are recorded on the work item.",
  "type": "bug"
}
Expected result

The payload uses supported App API fields: project_key, title, description, acceptance_criteria and type. It does not choose a reporter or impersonate a member; attribution is set by the server.

04

Read current Required Context

Request baseline reading for SUPPORT. Read every returned revision body before continuing. Inspect data.state and the complete guidance. If the response fails or a source cannot be read, stop and resolve access or policy health.

If no policy applies, data.receipt is null and data.revisions is empty. Otherwise the receipt lasts 24 hours and is bound to this workspace, App, owner, settings version, target and exact policy context. It records delivered reading, not understanding.

After reading, use jq to add the returned receipt and freeze task-create.json. Keep that exact file for delivery retries. Changed policies or permissions require fresh reading and a new logical write.

Read and retain Required Context
curl --fail-with-body --silent --show-error \
  "$TELLENZE_APP_API/required-context" \
  -H "Authorization: Bearer $TELLENZE_APP_TOKEN" \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  --data '{"project_key":"SUPPORT"}' \
  --output required-context.json
jq '.data | {state, expires_at}' required-context.json
jq -r '.data.revisions[] | "# " + .title + "\n\n" + (.body // "")' required-context.json
After reading: prepare the exact write body
jq --slurpfile context required-context.json \
  '. + {required_context_receipt: $context[0].data.receipt}' \
  task-definition.json > task-create.json
Expected result

HTTP 200 delivers the context under data. task-create.json now contains the same definition plus required_context_receipt, which may be null when reading does not apply.

05

Create once and save the returned identity

Use one stable, unique key for this external request and logical create. The example key is for ticket-1843 in this fictional integration. In a real integration, reserve the key and exact JSON body in durable storage before sending.

Save the response and map external_id to data.identifier and data.id. Do not infer the work identifier from the source ticket number. The API assigns it.

Create the task
curl --fail-with-body --silent --show-error \
  "$TELLENZE_APP_API/work-items" \
  -H "Authorization: Bearer $TELLENZE_APP_TOKEN" \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: support-bridge.ticket-1843.create-v1' \
  --data-binary @task-create.json \
  --dump-header task-create.headers \
  --output task-created.json
jq '.data | {id, identifier, version, title, origin}' task-created.json
Illustrative HTTP 201 response excerpt
{
  "data": {
    "identifier": "SUPPORT-42",
    "title": "Payment confirmation email is missing",
    "version": 1,
    "origin": {
      "kind": "app",
      "name": "Support bridge"
    }
  }
}
Expected result

HTTP 201 returns the created resource. Your actual identifier and App name will differ. origin includes the App and responsible member, captured by the server.

06

Recover a lost response with the same request

If the response is lost, resend the same method, URL, idempotency key and unchanged task-create.json. Do not fetch new context, rebuild the file or choose a new key just because the delivery result is unknown.

A successful replay returns the existing work and Idempotency-Replayed: true. It rechecks current grants and returns current resource values, so the task may have a newer version than its original creation response. Reusing a key for different content returns 409.

Exact retry after an uncertain delivery
curl --fail-with-body --silent --show-error \
  "$TELLENZE_APP_API/work-items" \
  -H "Authorization: Bearer $TELLENZE_APP_TOKEN" \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: support-bridge.ticket-1843.create-v1' \
  --data-binary @task-create.json \
  --dump-header task-create.headers \
  --output task-created.json
Expected result

If creation already succeeded, the retry returns HTTP 201 with the same resource identity and a replay header, without another task.

07

Read the saved task and check the definition

Read the returned identifier using the same authorized App. Compare the saved title, source URL, problem boundary, acceptance criteria and type with task-definition.json. Confirm origin identifies the intended App and member.

Store the external ID mapping and current version in your integration, then hand the task to the project’s work process. A successful intake does not mean the payment bug is fixed. Recording real verification, changing workflow stages and shipping work are later decisions; this API does not perform those actions.

Verify the returned work item
TASK_IDENTIFIER=$(jq -er '.data.identifier' task-created.json)
curl --fail-with-body --silent --show-error \
  "$TELLENZE_APP_API/work-items/$TASK_IDENTIFIER" \
  -H "Authorization: Bearer $TELLENZE_APP_TOKEN" \
  -H 'Accept: application/json' \
  --output task-verified.json
jq '.data | {identifier, version, title, description, acceptance_criteria, type, status, origin}' task-verified.json
jq -n --arg external_id 'ticket-1843' \
  --slurpfile saved task-verified.json \
  '{external_id: $external_id, work_item_id: $saved[0].data.id, work_item_identifier: $saved[0].data.identifier, version: $saved[0].data.version}' > ticket-1843-mapping.json
cat ticket-1843-mapping.json
Expected result

HTTP 200 returns the accessible saved work. Retain its actual identifier, current version and attribution. Compare all definition fields before calling this integration successful.

Verification

Check the saved result.

Use real returned records and evidence. The example responses describe the expected shape; your fresh read confirms what is saved now.

  • The saved external mapping points to the resource returned by the create or replay.
  • A fresh GET contains the intended source, scope and acceptance checks, and shows the expected App/member origin.
  • The original successful create and exact retry resolve to the same resource identity; inspect Idempotency-Replayed on the retry.
  • The work remains open in the project process. Any missing business evidence or investigation outcome remains visible.

When a step cannot continue

Keep the intent. Resolve the failure.

Read the returned error before choosing the next action. A lost response and a confirmed rejection need different recovery steps.

401, 403 or 404
Check the workspace address, token, active owner, operation permission and current project/document grants. Do not broaden access merely to bypass a failure.
409 after a confirmed rejection
Read the error body. A changed policy needs new reading; a version or idempotency conflict needs reconciliation. Correct the problem, review the revised request and use a new key only for that new logical write.
Lost response or uncertain delivery
Keep the saved body and key unchanged. Retry exactly, inspect the replay header, and reconcile the returned identity with your external mapping.
415 or 422
Send JSON and correct the named fields. Check the enabled work type. A corrected request is a new logical write with a new key.
429
Respect Retry-After. App limits are 120 requests/minute per credential and 240/minute per IP; retain the write identity while waiting.

Portable Markdown · No account or email needed

Take the complete recipe.

The pack includes setup, fictional inputs, every request, expected results, verification, recovery and the canonical reference links. Replace the example facts and values before using it.

Download Markdown pack

The download works without JavaScript.

Read the full Markdown pack

Keep the reference close

Use the current source.

COME TAKE A LOOK

A little less busy.
A lot more together.

Curious? Join the waitlist and see how we welcome your team.

Tell us a little about your team. We’ll explain the next steps and invite you when we have capacity for your workspace.

Join waitlist Start with your email. No payment needed to join.
Ask Tellenze