Documentation sections

Admin API: offers

Admin API methods for offers: list, create and edit, copies for team members, groups, archive upload, and the files and versions of a hosted offer.

This page covers 22 methods for working with offers. All of them require a token with Full access («Полный доступ») (full). Authorization is described on the Admin API: overview and authorization page, and the request format and common error codes on the Request, response and error format page.

Offer object

The list, read, create, edit and copy methods return an offer in the same form.

FieldTypeDescription
idstring, UUIDOffer ID. Used in method addresses
seqnumberThe offer's sequence number in the panel
namestringName
network_idstring or nullAffiliate network ID
urlstringLink of an external offer
storagestringexternal — an external link, local — the files are stored in the tracker. The default is external
delivery_typestringDelivery type: redirect, meta, js, direct, double_meta. The default is redirect
payoutnumberFixed payout. The default is 0
payout_typestringPayout model: cpa, cpc, crg, revshare. The default is cpa
payout_from_parambooleantrue — take the payout from a postback parameter. The default is false
payout_paramstringName of the postback parameter with the payout. The default is payout. Does not apply to conversion import from CSV
currencystringOffer currency, a three-letter code. The fixed payout payout is recorded in it. The default is USD
countrystring or nullTwo-letter country code
daily_capnumber or nullDaily sales cap, an integer from 0. null — no cap
cap_timezonestringTime zone of the cap in IANA format, for example Europe/Warsaw. The default is UTC
overflow_offer_idstring or nullFallback offer that receives clicks after the cap is reached
statestringStatus: active, paused, archived. The default is active
valuesarrayOffer values: objects {"name": "...", "value": "..."} for the {offer_value:name} macro. The default is []
group_namestringGroup name. An empty string — no group
owner_idstring or nullThe user the offer belongs to
php_networkbooleanPHP with network access. When a hosted offer is created, the default is true; an explicit false turns the network off. For an external offer — false. When the offer is edited without this field, the previous value is kept

Delivery types, payout models and statuses are explained on the Offers page, the cap and the fallback offer on the Daily sales cap and fallback offer page, and php_network on the PHP in hosted pages and network access page.

The fixed payout payout is recorded in the currency from the currency field and does not depend on the currency parameter of the postback. For an amount from the postback, the offer currency is used only if the network did not send its own currency. If currency holds a currency other than USD, it needs an exchange rate: without one, postbacks for an offer with a fixed payout get 422 with code currency_rate. Rates and conversion are covered on the Currencies and rates page.

payout_param applies to the incoming postback. Conversion import from CSV always takes the amount from the payout column.

If there is no offer with the given id, any method with {id} in its address responds 404 with code not_found. For an id that is not in UUID format, the response is 400 with code bad_request.

Offers

List offers

GET /api/v1/offers

Returns an array of offers, newest first. There are no parameters and no pagination. Deleted offers are not included. Response — code 200.

Get an offer

GET /api/v1/offers/{id}

Returns one offer. Response — code 200.

Create an offer

POST /api/v1/offers

Creates an offer. The user who created the token becomes its owner.

FieldTypeRequiredDescription
namestringyesName
urlstringyes, if storage is not localOffer link
storagestringnoexternal or local
daily_capnumber or nullnoAt least 0
cap_timezonestringnoIANA time zone, up to 64 characters. The value Local is not accepted
overflow_offer_idstring or nullnoAnother offer of this team with the active status

The other fields from the Offer object section, except id, seq and owner_id, are optional. Omitted fields get their default values.

bash
curl -X POST https://panel.example.com/api/v1/offers \
  -H "Api-Key: <token>" \
  -H "Content-Type: application/json" \
  -d '{"name":"Offer PL","url":"https://offer.example.com/?sub={subid}","payout":12.5,"country":"PL","daily_cap":50,"cap_timezone":"Europe/Warsaw"}'

Response — code 201 and the created offer.

Errors with code 422 and validation:

MessageReason
name is requiredThe name is empty
url is required for external offerAn external offer has no link
daily_cap must be non-negativeNegative cap
cap_timezone must be a valid IANA time zoneUnknown time zone
overflow_offer_id must identify another offerThe fallback is the offer itself or not a UUID
overflow offer must belong to this workspaceThe fallback offer was not found
overflow offer must be activeThe fallback offer does not have the active status

Edit an offer

PUT /api/v1/offers/{id}

Changes the fields you pass. Fields that are not in the body keep their current values. A null value clears country, daily_cap, network_id and overflow_offer_id. The group is removed with an empty string in group_name, and the values with an empty array in values. An empty string in storage, delivery_type, payout_type, payout_param, currency and state keeps the previous value, and in cap_timezone it sets UTC back.

The fields and errors are the same as when creating. The checks apply to the resulting offer. Response — code 200 and the updated offer.

Delete an offer

DELETE /api/v1/offers/{id}

Moves the offer and its files to the trash. Response — code 204 with no body. Restoring is described on the Admin API: trash page.

Copies and access

Clone an offer

POST /api/v1/offers/{id}/clone

Creates a copy of the offer. The suffix “ (copy)” (« (копия)») is added to the name. For a hosted offer, the files are copied too.

FieldTypeRequiredDescription
owner_idstringnoWho to assign the copy to. Without the field, the copy goes to the token's creator

Response — code 201 and the new offer.

Give copies to team members

POST /api/v1/offers/{id}/share-clone

Creates a separate copy of the offer for each team member in the list. The copy's name is the same as the original's, and the files of a hosted offer are copied.

FieldTypeRequiredDescription
user_idsarray of stringsyesUser IDs, at least one

Response — code 200 and an object with a results array. Each element has user_id, login and the offer_id of the new copy. If a copy could not be created, error comes instead of offer_id: “user not found” («пользователь не найден»), “could not clone” («не удалось клонировать») or “could not copy the files” («не удалось скопировать файлы»).

An empty list gives 422 with the message “no team member selected” («не выбран ни один сотрудник»).

Who the offer is shared with

GET /api/v1/offers/{id}/shares

Returns an object with a user_ids array — the users the offer is shared with for viewing. Response — code 200.

Share the offer with users

POST /api/v1/offers/{id}/shares

Replaces the list entirely. An empty array closes access for everyone.

FieldTypeRequiredDescription
user_idsarray of stringsyesThe new full list. Users of another team are skipped

Response — code 204 with no body.

Offer groups

A group is the value of the group_name field. To add an offer to a group, pass the group name in group_name when you create or edit the offer. Separate methods rename and delete a group across all its offers at once.

Rename a group

PUT /api/v1/offer-groups?name=<current name>

FieldTypeRequiredDescription
name in the addressstringyesCurrent group name
name in the bodystringyesNew name
bash
curl -X PUT "https://panel.example.com/api/v1/offer-groups?name=Nutra" \
  -H "Api-Key: <token>" \
  -H "Content-Type: application/json" \
  -d '{"name":"Nutra PL"}'

Response — code 200 and {"updated": 4}, where the number is how many offers were changed.

Delete a group

DELETE /api/v1/offer-groups?name=<name>

Removes the group from the offers. The offers themselves stay. No body is needed. Response — code 200 and {"updated": N}.

Errors of both methods — 422 with code validation: “name is required” («name обязателен») if the parameter is missing from the address. Renaming has two more: “invalid request body” («некорректное тело запроса») if the body is not a JSON object, and “new group name is required” («новое имя группы обязательно») if the name in the body is empty.

Hosted offer files

File paths are given relative to the offer folder, separated by /, for example css/style.css. After the first upload of an archive or file, or the first file save, the offer switches to storage: local. How the tracker serves such pages is described on the Hosted pages page.

Upload an archive

POST /api/v1/offers/{id}/upload

Accepts a ZIP as multipart/form-data and replaces all of the offer's files with it. The offer's lead log is kept.

FieldTypeRequiredDescription
archivefileyesZIP without a password
bash
curl -X POST https://panel.example.com/api/v1/offers/7c9e6679-7425-40de-944b-e07fc1f90ae7/upload \
  -H "Api-Key: <token>" \
  -F "[email protected]"

If the offer already had files, they are saved as a version before the replacement.

Response — code 200 and an object with a files array. A list element: path — the path, size — the size in bytes, dir — always false. The list is sorted by path; folders are not listed as separate rows.

If the archive is not accepted, the offer's previous files stay in place. Limits and errors are listed in the Upload limits and errors section.

List files

GET /api/v1/offers/{id}/files

Returns an object with a files array in the same form. For an offer without files, the array is empty. Response — code 200.

Add more files

POST /api/v1/offers/{id}/files

Adds files to the ones already uploaded, format — multipart/form-data. A file with the same path is replaced; the others are not affected.

FieldTypeRequiredDescription
filesfile, several allowedyesFiles. Only the file name is kept, without the folders from the original path
dirstringnoFolder inside the offer. Without the field, the files go to the root

Response — code 200 and an object: files — the full list of files, saved — the paths of the saved files, storage — local.

Read a file

GET /api/v1/offers/{id}/file?path=<path>

Returns the contents of a file up to 5 MB in size.

FieldTypeRequiredDescription
path in the addressstringyesFile path

Response — code 200 and an object: path, content — the contents as a string, utf8 — false if the file is not UTF-8 text.

Save a file

PUT /api/v1/offers/{id}/file

Creates or overwrites a text file. The body is JSON.

FieldTypeRequiredDescription
pathstringyesFile path. Missing folders are created
contentstringnoContents, up to 5 MB
createbooleannotrue — create only: if the file already exists, the response is 409 “a file with this path already exists” («файл с таким путём уже есть»)

Response — code 200 and {"ok": true, "storage": "local"}.

Delete a file

DELETE /api/v1/offers/{id}/file?path=<path>

Deletes one file. Response — code 204 with no body.

Rename a file

POST /api/v1/offers/{id}/rename

Renames a file or moves it to another folder. A file that already exists at the new path is not overwritten. The leads.php lead log and names starting with a dot are not renamed: the response is 400.

FieldTypeRequiredDescription
fromstringyesCurrent path
tostringyesNew path

Response — code 200 and {"ok": true}. Without one of the fields — 400 with the message “the source and new path are required” («нужны исходный и новый путь»).

File method errors

CodeMessageReason
422path is requiredThe path was not passed
404“file not found” («файл не найден»)Reading or deleting: there is no file at this path
400“invalid path” («недопустимый путь»)The path goes outside the offer folder
400“hidden files (starting with a dot) are not supported” («скрытые файлы (начинающиеся с точки) не поддерживаются»)The path contains a name that starts with a dot
413“file larger than 5 MB — the editor does not open or save such files; replace it by uploading a file or archive” («файл больше 5 МБ — Редактор такие не открывает и не сохраняет; замените его загрузкой файла или архива»)Reading or saving a file larger than 5 MB
413“too many files” («слишком много файлов»)A new file when the offer already has 10,000 files

Versions and archive

A version is a saved copy of the offer's files. The tracker keeps the offer's 10 latest versions; older ones are deleted when a new one is saved. The lead log and files whose names start with a dot are not included in versions or in the archive. Saving a version and downloading the archive are meant for an offer that has files. Working with versions in the panel is described on the File editor, versions and export page.

List versions

GET /api/v1/offers/{id}/versions

Returns an array of versions, newest first. Fields: id, created_at — creation time in UTC, size — size in bytes. If there are no versions, the array is empty. Response — code 200.

Save a version

POST /api/v1/offers/{id}/versions

Saves the current files as a new version. No body is needed. Response — code 201 and the version object.

Restore a version

POST /api/v1/offers/{id}/versions/{version}/restore

Replaces the offer's files with the files of the version. Put the id from the version list into {version}. Before the replacement, the current files are saved as a new version. The lead log stays in place. No body is needed. Response — code 200 and {"ok": true}.

Warning.

Restoring replaces all of the offer's current files. Only 10 versions are kept: after several restores and uploads in a row, old versions are deleted.

Download the archive

GET /api/v1/offers/{id}/archive

Returns the offer's current files as a single ZIP file, page.zip. Response — code 200 with the application/zip type.

bash
curl https://panel.example.com/api/v1/offers/7c9e6679-7425-40de-944b-e07fc1f90ae7/archive \
  -H "Api-Key: <token>" \
  -o page.zip

Saving a version and downloading the archive respond 413 if the offer has more than 10,000 files or more than 1 GB.

Upload limits and errors

What is limitedLimit
Size of one upload request1 GB
One file1 GB
All archive files after extraction1 GB
Number of files in the archive and in the offer10,000
File for reading and saving through /file5 MB

Archive requirements are on the Hosted pages page.

CodeMessageReason
413“1 GB upload limit exceeded” («превышен лимит загрузки 1 ГБ»)The request is larger than 1 GB
413“file too large (limit 1 GB per file, 1 GB per extracted archive)” («файл слишком большой (лимит 1 ГБ на файл, 1 ГБ на распакованный архив)»)A file or the extracted archive exceeds the limit
413“too many files”The archive or the offer has more than 10,000 files
422“expected a file in the archive field” («ожидался файл в поле archive»)The form has no archive field
422“expected files in the files field” («ожидались файлы в поле files»)The form has no files field
422“nothing to save” («нечего сохранять»)None of the files has a name
422“the file is not a valid .zip” («файл не является корректным .zip»)The uploaded file is not a ZIP
422“the archive uses an unsupported compression method (LZMA, AES encryption, etc.) — rebuild it as a regular ZIP without a password” («архив сжат неподдерживаемым методом (LZMA, AES-шифрование и т.п.) — пересоберите обычным ZIP без пароля»)The archive has a password or non-standard compression
422“the archive is damaged — rebuild it and upload it again” («архив повреждён — пересоберите его и загрузите заново»)The archive cannot be read to the end
400“could not read the form (the upload was interrupted or damaged)” («не удалось прочитать форму (загрузка прервана или повреждена)»)The transfer broke off or the body is not multipart/form-data
Updated Need help? ↗