Projects, placements and amplification with the API
Follow one project from draft to placement report and a separate amplification purchase. Each step shows which values to carry into the next request.
Connect to the Zapost.im API
Ask support for a live zp_live_ key. The full workflow requires read, projects:write and projects:pay. Replace the sample URL in the draft with your target page. Run the commands in order in one Bash session with curl, jq and Node.js 24.
set -o pipefail
API_BASE='https://zapost.im/api/v2'
read -rsp 'API key: ' API_KEY
printf '\n'
api() {
local path=$1
shift
curl --silent --show-error --fail-with-body \
-H "Authorization: Bearer $API_KEY" "$@" "$API_BASE$path"
}
post() {
api "$1" -H "Idempotency-Key: $2" \
-H 'Content-Type: application/json' --data "$3"
}
new_key() { node -p 'crypto.randomUUID()'; }
api /packages | jq '.data[] | {code, name, price, placements, page_limit}'
api /balance | jq '.data | {available, currency}'Choose a code from the catalog. This example uses the start package. All amounts in responses are in integer kopecks.
Create a project
Send the package, target page, site description and placement requirements. Keep the project id and revision from the response. You will purchase amplification separately after retrieving placements.
PACKAGE_CODE='start'
CREATE_KEY=$(new_key)
DRAFT=$(jq -cn --arg package "$PACKAGE_CODE" --arg ref "demo-$CREATE_KEY" '{
package_code: $package, external_id: $ref,
urls: ["https://example.test/catalog"],
brief: "Online shop for camping equipment",
notes: "Link to the catalog in a relevant discussion",
amplification_level: null
}')
PROJECT=$(post /projects "$CREATE_KEY" "$DRAFT")
PROJECT_ID=$(jq -er '.data.id' <<< "$PROJECT")
REVISION=$(jq -er '.data.revision' <<< "$PROJECT")
jq '.data | {id, revision, status}' <<< "$PROJECT"Generate a separate Idempotency-Key for each new action. If the outcome is unknown, repeat only the post line with the same key and body. Save those values if you plan to retry in a different session.
Review the package price and balance
Read the selected package price from the catalog and check the available balance. Show the price for approval before paying. Amplification is purchased separately in this example.
EXPECTED_AMOUNT=$(api /packages | jq -er --arg code "$PACKAGE_CODE" \
'.data[] | select(.code == $code) | .price')
jq -n --argjson amount "$EXPECTED_AMOUNT" '{expected_amount: $amount}'
api /balance | jq '.data | {available, currency}'Confirm project payment
After approval, send the project revision and expected_amount in integer kopecks. This request charges the account balance. Check the result by payment_id.
PAY_KEY=$(new_key)
PAY_BODY=$(jq -cn --argjson revision "$REVISION" --argjson amount "$EXPECTED_AMOUNT" \
'{revision: $revision, expected_amount: $amount}')
PAYMENT=$(post "/projects/$PROJECT_ID/payments" "$PAY_KEY" "$PAY_BODY")
PAYMENT_ID=$(jq -er '.data.id' <<< "$PAYMENT")
api "/projects/$PROJECT_ID/payments/$PAYMENT_ID" |
jq '.data | {id, status, amount, currency}'A 409 revision_conflict or amount_changed response leaves the balance unchanged. Reload the project and catalog and show the amount for approval again. If funds are insufficient, top up manually. After a confirmed failure, use a new key for the new payment attempt.
Read progress and placements
After payment, read the project status and progress. The report can be empty immediately after purchase. A link becomes available when the manager adds it to the report. The manager marks the project complete separately. This example uses limit=1 to show how to request the next page.
api "/projects/$PROJECT_ID" | jq '.data | {status, progress}'
PLACEMENTS="/projects/$PROJECT_ID/placements"
PAGE=$(api "$PLACEMENTS?limit=1")
jq '{data, progress, revision, pagination}' <<< "$PAGE"
NEXT=$(jq -r '.pagination.next_cursor // empty' <<< "$PAGE")
REPORT_REVISION=$(jq -er '.revision' <<< "$PAGE")
if [ -n "$NEXT" ]; then
PAGE=$(api "$PLACEMENTS" --get --data-urlencode 'limit=1' \
--data-urlencode "cursor=$NEXT" \
--data-urlencode "revision=$REPORT_REVISION")
jq '{data, progress, revision, pagination}' <<< "$PAGE"
fi{
"id": "10000000-0000-4000-8000-000000000001",
"url": "https://forum.example.test/topic/1#message-7",
"target_url": "https://example.test/catalog",
"recorded_at": "2026-11-06T12:00:00Z",
"verification_status": "unchecked"
}Keep each placement id. A change to the url and target_url pair creates a new id. recorded_at is the time the link was added to the report. For subsequent pages use next_cursor from the latest response and keep revision and limit. On 409 placements_changed, start again. Replace your local list only after reaching next_cursor=null successfully.
Choose an amplification level
Reuse the same PROJECT_ID. Check amplification.can_purchase in a fresh project response. Choose an available level from the amplification catalog for the purchased package. This example uses basic. Review the price of this level before paying.
PROJECT=$(api "/projects/$PROJECT_ID")
REVISION=$(jq -er '.data.revision' <<< "$PROJECT")
jq '.data.amplification | {can_purchase, unavailable_reason}' <<< "$PROJECT"
LEVEL='basic'
AMP_AMOUNT=$(api /amplification-packages | jq -er \
--arg code "$PACKAGE_CODE" --arg level "$LEVEL" \
'.data[] | select(.package_code == $code) | .levels[] | select(.level == $level) | .price')
jq -n --argjson amount "$AMP_AMOUNT" '{expected_amount: $amount}'Pay for amplification and check its status
After separate approval, send level, the project revision and expected_amount for amplification in integer kopecks. This payment covers amplification only. Check the payment result and read the amplification state from the project.
AMP_PAY_KEY=$(new_key)
AMP_PAY_BODY=$(jq -cn --argjson revision "$REVISION" --arg level "$LEVEL" \
--argjson amount "$AMP_AMOUNT" '{revision: $revision, level: $level, expected_amount: $amount}')
AMP_PATH="/projects/$PROJECT_ID/amplification"
AMP_PAYMENT=$(post "$AMP_PATH/payments" "$AMP_PAY_KEY" "$AMP_PAY_BODY")
AMP_PAYMENT_ID=$(jq -er '.data.id' <<< "$AMP_PAYMENT")
api "/projects/$PROJECT_ID/payments/$AMP_PAYMENT_ID" |
jq '.data | {id, purpose, status, amount, currency}'
api "/projects/$PROJECT_ID" |
jq '.data.amplification | {purchase, current}'The payment result has purpose=amplification. A successful payment confirms the purchase. Check current.status and current.started_at to see whether amplification is waiting for publications or has started.