{"openapi":"3.1.0","info":{"title":"Comunicate.top API","version":"1.0.0","description":"Publish articles (advertorials, press releases) on 3.800+ websites in Romania, Italy and beyond: catalogue, articles, publications, campaigns, reports.\n\n**Free, no key needed.** The routes under `/public/` are open to anyone and read-only: niches, catalogue figures and market statistics (authority distribution, turnaround times, price per campaign type). No sign-up, no rate-limit headers to negotiate. There is deliberately no public route that returns the full publication list.\n\n**Everything else** needs an API key issued in Integrations, sent as `Authorization: Bearer bk_live_…`, or an OAuth 2.1 access token. Writing anything — articles, orders, publications — always requires a key. Also available as an MCP server at https://app.comunicate.top/mcp.","contact":{"url":"https://comunicate.top/ro/contact"}},"servers":[{"url":"https://app.comunicate.top/api/v1"}],"tags":[{"name":"Public catalogue","description":"Four routes that work with no key at all: the network’s niches, one niche with sample publications, the catalogue figures and the market statistics. This is the same data the public site shows, served as JSON — useful for comparisons, for an assistant that wants to know what is in the network, or for anyone who wants to see the shape of the data before opening an account. There is deliberately no public route for the full publication list: the inventory is not downloadable in one request."},{"name":"Key and permissions","description":"The first call of any integration: who the key is and what it may do. This is also where you see whether the organisation enabled \"AI assistants may order and publish\" — the switch that decides what an MCP assistant can do with the account’s money."},{"name":"Catalogue","description":"Which publications exist, what each campaign type costs, and whether your packages cover them."},{"name":"Articles","description":"An article enters the platform four ways: written by you as HTML, imported from a document, taken from a Drive folder, or written by the platform from a brief. All produce the same thing — a draft that can be published."},{"name":"Writing orders","description":"The API starts no generation. You place an order, we see it, we write it or decline it with a reason, and you learn the outcome the same way.\n\nThe reason is simple: a key runs inside a script and can ask for a thousand articles overnight, out of quotas that belong to the platform and are shared by every client. And a model-written text delivered automatically, seen by nobody here, goes out to publishers under our name."},{"name":"Checks","description":"What can be said about an article without calling any model: the SEO analysis and the campaign-type fit. Both are rule-based, so they cost nothing, are not rate-limited, and give the same answer every time.\n\nThey **report**; they fix nothing. Filling things in stays yours, through a `PATCH`."},{"name":"Media"},{"name":"Publications","description":"A publication ties an article to a publisher site. It is the only place in the API that spends credits or money."},{"name":"Campaigns","description":"Grouping articles and publications by client or project. The equivalent of \"projects\" on other platforms."},{"name":"Balance and reports"},{"name":"Domain audit","description":"A full check of a domain: PageSpeed on mobile and desktop with every category, real Chrome field data, Safe Browsing, and — when we have access to the property — Search Console."}],"paths":{"/public/stats":{"get":{"operationId":"get__public_stats","summary":"Catalogue figures","description":"How many active publications the network has, and across how many niches. No key needed.\n\nNo authentication required: this route is open to anyone.","tags":["Public catalogue"],"parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"publications":{"type":"integer","description":"Active publications."},"niches":{"type":"integer","description":"Niches with publications."}}}}}},"400":{"description":"Invalid input"}},"security":[]}},"/public/niches":{"get":{"operationId":"get__public_niches","summary":"The network’s niches","description":"All niches, with the publication count and average authority of each. No key needed.\n\nNo authentication required: this route is open to anyone.","tags":["Public catalogue"],"parameters":[{"name":"locale","in":"query","required":false,"description":"Language of names and descriptions. Defaults to `ro`.","schema":{"type":"string","enum":["ro","en"]}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"slug":{"type":"string","description":"The niche identifier, used in the next route."},"name":{"type":"string","description":"The niche name in the requested language."},"description":{"type":"string","description":"What kind of material the publications in this niche accept."},"publicationCount":{"type":"integer","description":"How many active publications the niche has."},"averageDa":{"type":"integer","description":"Average domain authority (Moz DA)."}}}}}},"400":{"description":"Invalid input"}},"security":[]}},"/public/niches/{slug}":{"get":{"operationId":"get__public_niches_slug","summary":"One niche, with sample publications","description":"The requested niche and a few of its publications, with authority, turnaround, price and how paid content is labelled. It is a sample, not the inventory: the full list is visible from an account.\n\nNo authentication required: this route is open to anyone.","tags":["Public catalogue"],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"locale","in":"query","required":false,"description":"Language of names and descriptions. Defaults to `ro`.","schema":{"type":"string","enum":["ro","en"]}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"slug":{"type":"string","description":"The niche identifier."},"name":{"type":"string","description":"The niche name."},"publicationCount":{"type":"integer","description":"How many publications the niche has in total."},"averageDa":{"type":"integer","description":"The niche’s average authority."},"publications":{"type":"array","items":{"type":"object","additionalProperties":true},"description":"The sample: `domain`, `language`, `metricDa`, `metricPa`, `deliveryDays`, `priceCents`, `currency`, `marcaj`."}}}}}},"400":{"description":"Invalid input"}},"security":[]}},"/public/statistici-piata":{"get":{"operationId":"get__public_statistici_piata","summary":"Market statistics","description":"The catalogue’s aggregate figures: how many publications, how many from our own network, the authority distribution, turnaround times and the price for each campaign type. The data behind the “What an advertorial costs” page.\n\nNo authentication required: this route is open to anyone.","tags":["Public catalogue"],"parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"publicatii":{"type":"integer","description":"Active publications with a public price."},"retea":{"type":"integer","description":"How many of them are our own."},"autoritate":{"type":"object","additionalProperties":true,"description":"Minimum, maximum, median, mean and the distribution across bands."},"termene":{"type":"array","items":{"type":"object","additionalProperties":true},"description":"How many publications deliver in how many days."},"preturi":{"type":"array","items":{"type":"object","additionalProperties":true},"description":"Per campaign type: price, minimum, maximum and how many publications accept it."}}}}}},"400":{"description":"Invalid input"}},"security":[]}},"/partner/me":{"get":{"operationId":"get_me","summary":"Who am I","description":"The organisation behind the key, its permissions and the AI assistants switch. Requires no scope: any valid key may ask.\n\nNo scope required.","tags":["Key and permissions"],"parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"organization":{"type":"object","additionalProperties":true,"description":"`id` and `name`."},"scopes":{"type":"array","items":{"type":"string"},"description":"The key’s scopes."},"aiPublishing":{"type":"boolean","description":"`true` if AI assistants may order publications and writing (Integrations → AI assistants)."}}}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key lacks the required scope"}},"security":[{"apiKey":[]},{"oauth2":[]}]}},"/partner/catalog":{"get":{"operationId":"get_catalog","summary":"List publications","description":"The paginated catalogue, with the same filters as the interface. Each entry also states how it can be paid for: from a package, from the balance, or not at all.\n\nRequired scope: `CATALOG_READ`.","tags":["Catalogue"],"parameters":[{"name":"page","in":"query","required":false,"description":"Requested page, from 1.","schema":{"type":"integer"}},{"name":"pageSize","in":"query","required":false,"description":"Entries per page, at most 100.","schema":{"type":"integer"}},{"name":"search","in":"query","required":false,"description":"Searches name and domain.","schema":{"type":"string"}},{"name":"campaign","in":"query","required":false,"description":"Only publications accepting the given type (see `campaign-types`).","schema":{"type":"string"}},{"name":"serviceType","in":"query","required":false,"description":"`PUBLISH_ONLY`, `WRITE_AND_PUBLISH`, `HOMEPAGE_PLACEMENT` or `FACEBOOK_SHARE`. For the other three, only sites with an active offer of the requested type appear — plain publishing is the base service, any priced site has it.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"entries[].id":{"type":"string","format":"uuid","description":"The publication id. Used when requesting a publication."},"entries[].metrics":{"type":"object","additionalProperties":true,"description":"DR, DA, PA and backlink count. `null` where we have no reading."},"entries[].audit":{"type":"string","enum":["object","null"],"description":"`flagged` is the only field that changes a decision: a site Google flags as dangerous should not be bought."},"entries[].preturiCampanii":{"type":"object","additionalProperties":true,"description":"Publication price per accepted campaign type, in minor units."},"entries[].access":{"type":"object","additionalProperties":true,"description":"`OWN`, `CREDIT` (covered by a package), `MONEY` (paid from balance), `UNAVAILABLE`. When `packages` has more than one entry, publishing requires `packageTypeId`."},"entries[].masurat":{"type":"object","additionalProperties":true,"description":"What we measured from our own orders: average hours to going live, how many verified publications are still fine. `null` where we have not published enough."}}}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key lacks the required scope"}},"security":[{"apiKey":[]},{"oauth2":["CATALOG_READ"]}]}},"/partner/catalog/{siteId}":{"get":{"operationId":"get_catalog_siteId","summary":"One publication","description":"The same shape as a list entry, plus what does not fit in a listing: per-campaign terms, accepted topics, active offers — writing, homepage placement, Facebook sharing. A missing offer for a type means the site does not sell it.\n\nRequired scope: `CATALOG_READ`.","tags":["Catalogue"],"parameters":[{"name":"siteId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key lacks the required scope"}},"security":[{"apiKey":[]},{"oauth2":["CATALOG_READ"]}]}},"/partner/campaign-types":{"get":{"operationId":"get_campaign_types","summary":"Campaign types","description":"A type is not a label but a set of conditions the article passes or fails on submission. Checking them up front saves a chain of one-by-one rejections.\n\nWhich types a given site accepts is a different question, answered by `acceptedCampaigns` in the catalogue.\n\nRequired scope: `CATALOG_READ`.","tags":["Catalogue"],"parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key lacks the required scope"}},"security":[{"apiKey":[]},{"oauth2":["CATALOG_READ"]}]}},"/partner/articles":{"post":{"operationId":"post_articles","summary":"Create an article from HTML","description":"The direct route, when you already have the text. The markup goes through the same sanitiser as imports: scripts, inline styles and elements that do not belong in an article are removed, and what was removed appears in the response.\n\nOptimisation fields are normalised, not rejected: five comma-separated keywords keep the first; ten tags keep the first three. An article supports one term, and twenty tags mean archive pages with a single text on them.\n\nRequired scope: `ARTICLES_WRITE`.","tags":["Articles"],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["title","contentHtml"],"properties":{"title":{"type":"string","description":"The article title."},"contentHtml":{"type":"string","description":"The article body, as HTML."},"focusKeyword":{"type":"string","description":"The keyword to optimise for. **One only** — from a list, the first is kept."},"tags":{"type":"array","items":{"type":"string"},"description":"At most three, however many are sent. A comma-separated list inside one element is also accepted."},"metaDescription":{"type":"string","description":"The description shown in search results."},"slug":{"type":"string","description":"The proposed article URL slug. WordPress may change it on collision; what actually resulted is read back and kept on the publication."},"excerpt":{"type":"string","description":"The summary themes use in listings and on category pages."},"featuredImageAlt":{"type":"string","description":"The featured image's alt text. Its absence is one of the findings the SEO analysis reports."},"seoTitle":{"type":"string","description":"The search-result title, when it differs from the article title."},"featuredImageUrl":{"type":"string","description":"The featured image. Can be the URL returned by `POST /partner/media`."},"campaignId":{"type":"string","format":"uuid","description":"The campaign the article belongs to."},"idempotencyKey":{"type":"string","description":"Chosen by you — a locally generated UUID is enough. Sent again, for the same organisation, it returns the article already created instead of making a new one. No effect on `PATCH`. Recommended for any code that might resend the request after a timeout."}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key lacks the required scope"}},"security":[{"apiKey":[]},{"oauth2":["ARTICLES_WRITE"]}]},"get":{"operationId":"get_articles","summary":"List articles","description":"The organisation's articles, most recently updated first. Cursor-paginated: the response's `nextCursor` is sent back as `cursor` for the next page, `null` on the last one.\n\nRequired scope: `ARTICLES_READ`.","tags":["Articles"],"parameters":[{"name":"cursor","in":"query","required":false,"description":"The id of the last article seen. Absent on the first page.","schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","required":false,"description":"How many articles per page.","schema":{"type":"integer"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key lacks the required scope"}},"security":[{"apiKey":[]},{"oauth2":["ARTICLES_READ"]}]}},"/partner/articles/import":{"post":{"operationId":"post_articles_import","summary":"Import a document","description":"Accepts `.docx`, `.doc`, `.odt`, `.rtf`, `.fodt`, `.html` and `.htm`. Older formats go through LibreOffice, so they take a few seconds longer.\n\n**File order matters**: the first file is the document, the second — optional — is the featured image. They cannot be told apart by field name, because everyone names their files differently.\n\nImages inside the document are pulled into the media library and rewritten in the text; those that could not be pulled appear in `images.skipReasons` with the reason. Metadata — description, tags — is filled in afterwards, in the background.\n\nRequired scope: `ARTICLES_WRITE`.","tags":["Articles"],"parameters":[],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["(primul fișier)"],"properties":{"(primul fișier)":{"type":"string","description":"The document."},"(al doilea fișier)":{"type":"string","description":"The featured image, for documents with none in the body."},"campaignId":{"type":"string","format":"uuid","description":"The campaign the article belongs to."},"folderId":{"type":"string","format":"uuid","description":"The media-library folder the document images land in."}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"articles[]":{"type":"string","description":"The articles created, with title and word count."},"images":{"type":"object","additionalProperties":true,"description":"How many images were imported, reused, converted, and why the rest were skipped."},"sanitized":{"type":"object","additionalProperties":true,"description":"What the sanitiser removed. If the article looks different from the document, this says why."}}}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key lacks the required scope"}},"security":[{"apiKey":[]},{"oauth2":["ARTICLES_WRITE"]}]}},"/partner/articles/drive":{"get":{"operationId":"get_articles_drive","summary":"List a Drive folder","description":"The folder must be shared \"anyone with the link\": the platform reads it with its own account, the client signs in nowhere.\n\nListing is deliberately a separate step from importing. A folder usually holds drafts, old versions and the good document — importing everything produces ten articles, nine of which get deleted.\n\nRequired scope: `ARTICLES_WRITE`.","tags":["Articles"],"parameters":[{"name":"link","in":"query","required":true,"description":"The folder link, as Google gives it.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key lacks the required scope"}},"security":[{"apiKey":[]},{"oauth2":["ARTICLES_WRITE"]}]},"post":{"operationId":"post_articles_drive","summary":"Import from the Drive folder","description":"At most fifty documents at a time. A broken document does not stop the rest: the result has one entry per file, with either the article count or its error.\n\nThe chosen image is downloaded once and used for every document that has none in its body.\n\nRequired scope: `ARTICLES_WRITE`.","tags":["Articles"],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["link","fisiere"],"properties":{"link":{"type":"string","description":"The same folder link used for the listing. Every requested file is checked against it: downloads use the platform account, which also sees other clients’ folders, so the route does not accept arbitrary Drive identifiers."},"fisiere":{"type":"string","description":"The chosen documents, with `id`, `nume` and `mimeType` from the listing."},"imagineId":{"type":"string","description":"The id of an image in the same folder, used as the featured image."},"campaignId":{"type":"string","format":"uuid","description":"The campaign the articles belong to."}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key lacks the required scope"}},"security":[{"apiKey":[]},{"oauth2":["ARTICLES_WRITE"]}]}},"/partner/articles/{articleId}":{"patch":{"operationId":"patch_articles_articleId","summary":"Update an article","description":"Every field from creation, all optional. Send only what changes. The same normalisations apply: one keyword, at most three tags.\n\n`version` increases **only when the title or the content changes**, not on every edit. It is part of the publication idempotency key: a corrected, resubmitted article is a new publication, whereas a changed tag does not produce a different article on the site.\n\nAn article currently being published cannot be edited: you get `409`. The worker works from the content read at the start of the process, and an edit now would put something different on the site than what the platform holds.\n\nRequired scope: `ARTICLES_WRITE`.","tags":["Articles"],"parameters":[{"name":"articleId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key lacks the required scope"}},"security":[{"apiKey":[]},{"oauth2":["ARTICLES_WRITE"]}]},"get":{"operationId":"get_articles_articleId","summary":"One article","description":"The whole article, with its text. This is also where you see whether a draft requested through `redactare` has finished: `redactedAt` set means done, and `redactionFindings` says what remains unresolved.\n\nRequired scope: `ARTICLES_READ`.","tags":["Articles"],"parameters":[{"name":"articleId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"redactedAt":{"type":"string","enum":["datetime","null"],"description":"When writing finished. `null` while queued."},"redactionFindings":{"type":"string","description":"What did not come out right, with `cod` and `mesaj`. An article with blocking findings can be submitted, but is likely to be rejected by the publisher."},"suggestedCampaignType":{"type":"string","enum":["string","null"],"description":"What kind of article it appears to be, after we read it. Deliberately separate from the type ordered: an article classified \"SEO\" but bought as a brand mention is a question to raise before payment, not a silent correction."},"classificationConfidence":{"type":"integer","description":"Out of 100. Rules alone give high confidence; when a model was needed to separate two candidates it is lower — and it shows."},"classificationReason":{"type":"string","enum":["string","null"],"description":"The reason, written for a person. It can be argued with."},"version":{"type":"integer","description":"Increases only when the title or content changes. Part of the publication idempotency key."},"status":{"type":"string","description":"`DRAFT`, `IN_REVIEW`, `APPROVED`, `SCHEDULED`, `PUBLISHING`, `PUBLISHED`, `FAILED`, `REJECTED`, `ARCHIVED`."},"createdViaApi":{"type":"boolean","description":"It came in through a key, not the interface. Everything you create through the API appears in the account like anything else — under Articles, Campaigns, Publications — with this marker beside it, so what the integration did is visible."}}}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key lacks the required scope"}},"security":[{"apiKey":[]},{"oauth2":["ARTICLES_READ"]}]},"delete":{"operationId":"delete_articles_articleId","summary":"Delete an article","description":"Responds `204` on success. Refused with `409` if the article has publications: a publication's history must be able to show what was sent, and a published article is removed from the list, not from the database.\n\nRequired scope: `ARTICLES_WRITE`.","tags":["Checks"],"parameters":[{"name":"articleId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key lacks the required scope"}},"security":[{"apiKey":[]},{"oauth2":["ARTICLES_WRITE"]}]}},"/partner/redactare":{"post":{"operationId":"post_redactare","summary":"Order an article","description":"The response is the order, with `status: \"NOUA\"`. When it is done, the same order has `status: \"LIVRATA\"` and `articleId` filled in.\n\n**It is paid from the money balance, not from credits.** A credit is “one publication included” from a package, not hours of writing. The price is reserved the moment the order arrives and consumed on delivery; a rejection, a cancellation or a writing failure on our side releases it in full. With no balance the order is refused with `400` and is not created at all — better a refusal at the door than an article written on credit.\n\n`fapte` is the one measured field that changes the quality of the text. With the client's data in front of it, the article uses their exact figures; without it, it stays general — correct, but less useful. The rule is strict: **only what appears there may appear in the article as a figure or a name**.\n\nRequired scope: `ARTICLES_WRITE`.","tags":["Writing orders"],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["tema","campaignType","cuvinte"],"properties":{"tema":{"type":"string","description":"One sentence, as you would tell a writer. The title comes from it."},"campaignType":{"type":"string","description":"One of the types from `campaign-types`."},"cuvinte":{"type":"string","enum":["500","700","1000","1500"],"description":"Target length."},"cuvantCheie":{"type":"string","description":"One only, one to four words."},"brand":{"type":"string","description":"The promoted brand."},"adresaPromovata":{"type":"string","description":"The URL the article points to."},"fapte":{"type":"string","description":"One item per entry: prices, years in business, certifications, who can be quoted."},"campaignId":{"type":"string","format":"uuid","description":"The campaign the delivered article joins."},"note":{"type":"string","description":"Anything else you want to tell us about the order."},"idempotencyKey":{"type":"string","description":"Chosen by you — a locally generated UUID is enough. Sent again with exactly the same data, it returns the order already created instead of making a new one. Recommended for any code that might resend the request after a timeout: without a key, a resend creates a second order and a second money reservation."}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key lacks the required scope"}},"security":[{"apiKey":[]},{"oauth2":["ARTICLES_WRITE"]}]},"get":{"operationId":"get_redactare","summary":"Your orders","description":"`status` filters. Most recent first.\n\nRequired scope: `ARTICLES_READ`.","tags":["Writing orders"],"parameters":[{"name":"status","in":"query","required":false,"description":"`NOUA`, `IN_LUCRU`, `LIVRATA`, `REFUZATA`, `ANULATA`.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key lacks the required scope"}},"security":[{"apiKey":[]},{"oauth2":["ARTICLES_READ"]}]}},"/partner/redactare/{orderId}":{"get":{"operationId":"get_redactare_orderId","summary":"Order status","description":"States: `NOUA` (received), `IN_LUCRU` (someone here picked it up), `LIVRATA` (`articleId` filled in, `chargedCents` says what was charged), `REFUZATA` (with `rejectionReason`), `ANULATA`, `ESUATA` (writing failed on our side). In the last three, the reserved amount returns to the balance.\n\n`articleId` is returned **only on delivery**. While it is being written the draft is ours: if the model produced something weak, it gets rewritten or declined, without anyone having had the chance to send it on.\n\nA `redaction.delivered` webhook saves you from polling — a human review sits between the request and the article, and its duration cannot be predicted.\n\nRequired scope: `ARTICLES_READ`.","tags":["Writing orders"],"parameters":[{"name":"orderId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key lacks the required scope"}},"security":[{"apiKey":[]},{"oauth2":["ARTICLES_READ"]}]}},"/partner/redactare/{orderId}/anuleaza":{"post":{"operationId":"post_redactare_orderId_anuleaza","summary":"Cancel an order","description":"Only while it is `NOUA`. Once someone here has picked it up the work is done or in progress and cancelling is no longer yours to do: you get `409`, and closing it stays a declined order with a reason, from our side.\n\nRequired scope: `ARTICLES_WRITE`.","tags":["Writing orders"],"parameters":[{"name":"orderId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key lacks the required scope"}},"security":[{"apiKey":[]},{"oauth2":["ARTICLES_WRITE"]}]}},"/partner/articles/{articleId}/seo":{"get":{"operationId":"get_articles_articleId_seo","summary":"SEO analysis","description":"Score, findings, statistics and the heading outline. The score is the percentage of checks passed, not a ranking promise.\n\nWhat matters in the response is `issues`: each has a `code`, a severity and a message saying what is missing. Fixes — an over-long title, a missing description, images without alt text — are written with `PATCH /partner/articles/{id}`.\n\nRequired scope: `ARTICLES_READ`.","tags":["Checks"],"parameters":[{"name":"articleId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"score":{"type":"integer","description":"0–100, the percentage of checks passed."},"issues[]":{"type":"string","description":"`code`, `severity` and `message`."},"stats":{"type":"object","additionalProperties":true,"description":"Words, headings, images without alt text, internal and external links, keyword density, and the lengths of title, description and slug."},"outline":{"type":"string","description":"The heading outline, with each level."}}}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key lacks the required scope"}},"security":[{"apiKey":[]},{"oauth2":["ARTICLES_READ"]}]}},"/partner/articles/{articleId}/potrivire":{"get":{"operationId":"get_articles_articleId_potrivire","summary":"Does it match the campaign type?","description":"A different question from the SEO analysis: that one says how well the text is written, this one says whether it is the article that was **ordered**. A flawless SEO text is a certain rejection in a brand-mention campaign, because it carries links.\n\nThe check stops the order at submission anyway. Without this route, the only way to find out was to try — article by article, from errors. The response also includes `cerinte`, so the message to the client is not merely \"does not match\".\n\nWhen the article sits in a campaign bound to a type, the verdict arrives on the article anyway, in `potrivireCampanie`; this is for a type you have not chosen yet.\n\nRequired scope: `ARTICLES_READ`.","tags":["Checks"],"parameters":[{"name":"articleId","in":"path","required":true,"schema":{"type":"string"}},{"name":"campaignType","in":"query","required":false,"description":"The type checked. Defaults to `SEO`.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key lacks the required scope"}},"security":[{"apiKey":[]},{"oauth2":["ARTICLES_READ"]}]}},"/partner/articles/{articleId}/revisions":{"get":{"operationId":"get_articles_articleId_revisions","summary":"Previous revisions","description":"The full text of every replaced version. A revision is written on each title or content change — exactly when `version` increases.\n\nIt exists for the moment a publisher says the article on the site no longer resembles what they accepted: answering that requires the whole text, not a sample from a log.\n\nRequired scope: `ARTICLES_READ`.","tags":["Checks"],"parameters":[{"name":"articleId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"curenta":{"type":"object","additionalProperties":true,"description":"The current version, so the first revision has something to compare against."},"revizii[]":{"type":"string","description":"At most fifty, most recent first. `changedBy` is empty when the change came through an API key."}}}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key lacks the required scope"}},"security":[{"apiKey":[]},{"oauth2":["ARTICLES_READ"]}]}},"/partner/media":{"post":{"operationId":"post_media","summary":"Upload an image","description":"JPEG, PNG, GIF, WebP or AVIF. The type is detected from content, not from the extension or header: a disguised file is rejected.\n\nThe same image uploaded twice is not duplicated — it is recognised by checksum and the existing one is returned. The URL from the response can go straight into an article's `featuredImageUrl`.\n\nRequired scope: `MEDIA_WRITE`.","tags":["Media"],"parameters":[],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["(fișier)"],"properties":{"(fișier)":{"type":"string","description":"The image."},"folderId":{"type":"string","format":"uuid","description":"The media-library folder."}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key lacks the required scope"}},"security":[{"apiKey":[]},{"oauth2":["MEDIA_WRITE"]}]}},"/partner/publications":{"post":{"operationId":"post_publications","summary":"Request publication","description":"One article, one or more publications.\n\n**Payment ambiguity is never guessed.** When several owned packages cover the same site, the request is rejected with `409` and the list of options; the integration picks one and retries with `packageTypeId`. Guessing would consume the wrong package, and that cannot be undone.\n\nRequired scope: `PUBLICATIONS_WRITE`.","tags":["Publications"],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["articleId","siteIds","campaignType"],"properties":{"articleId":{"type":"string","format":"uuid","description":"The article to publish."},"siteIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"The publications chosen from the catalogue."},"campaignType":{"type":"string","description":"The campaign type. Must be among the site's `acceptedCampaigns`."},"packageTypeId":{"type":"string","format":"uuid","description":"Which package pays, when several cover the site."},"licenseNumber":{"type":"string","description":"The operator’s ONJN gambling licence. Required when `campaignType` is `\"CASINO\"` — gambling advertising must display it, and the platform writes it onto the article itself before sending it."},"extrasBySite":{"type":"object","additionalProperties":true,"description":"Extras requested per site — `\"HOMEPAGE_PLACEMENT\"`, `\"FACEBOOK_SHARE\"`, at most both. The key is `siteId`, the value a list of types; each one requested must have an active offer on that site (see `GET /partner/catalog`), otherwise the request is rejected."},"scheduledFor":{"type":"string","format":"date-time","description":"When it should go live, if not immediately."}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key lacks the required scope"}},"security":[{"apiKey":[]},{"oauth2":["PUBLICATIONS_WRITE"]}]},"get":{"operationId":"get_publications","summary":"List publications","description":"All the organisation's publications. `status` filters. Cursor-paginated: the response's `nextCursor` is sent back as `cursor` for the next page, `null` on the last one.\n\nFor tracking state, webhooks are better: `publication.published` arrives with the article URL the moment it goes live, while polling burns your rate limit without learning anything extra.\n\nRequired scope: `PUBLICATIONS_READ`.","tags":["Publications"],"parameters":[{"name":"status","in":"query","required":false,"description":"`DRAFT`, `AWAITING_APPROVAL`, `APPROVED`, `PUBLISHING`, `PUBLISHED`, `FAILED`, `REJECTED`, `CANCELLED`.","schema":{"type":"string"}},{"name":"cursor","in":"query","required":false,"description":"The id of the last publication seen. Absent on the first page.","schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","required":false,"description":"How many publications per page.","schema":{"type":"integer"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key lacks the required scope"}},"security":[{"apiKey":[]},{"oauth2":["PUBLICATIONS_READ"]}]}},"/partner/publications/{publicationId}":{"get":{"operationId":"get_publications_publicationId","summary":"One publication","description":"The whole publication, with its event history and the reason, when it failed or was rejected.\n\nRequired scope: `PUBLICATIONS_READ`.","tags":["Publications"],"parameters":[{"name":"publicationId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key lacks the required scope"}},"security":[{"apiKey":[]},{"oauth2":["PUBLICATIONS_READ"]}]}},"/partner/campaigns":{"get":{"operationId":"get_campaigns","summary":"List campaigns","description":"`status` filters. Cursor-paginated: the response’s `nextCursor` is sent back as `cursor` for the next page, `null` on the last one.\n\nRequired scope: `CAMPAIGNS_READ`.","tags":["Campaigns"],"parameters":[{"name":"status","in":"query","required":false,"description":"`DRAFT`, `ACTIVE`, `PAUSED`, `COMPLETED`, `ARCHIVED`.","schema":{"type":"string"}},{"name":"cursor","in":"query","required":false,"description":"The id of the last campaign seen. Absent on the first page.","schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","required":false,"description":"How many campaigns per page.","schema":{"type":"integer"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key lacks the required scope"}},"security":[{"apiKey":[]},{"oauth2":["CAMPAIGNS_READ"]}]},"post":{"operationId":"post_campaigns","summary":"Create a campaign","description":"The body is strict: a field it does not know is rejected with `400`, not silently ignored. Better an error on the first attempt than a setting that looks applied and is not.\n\nRequired scope: `CAMPAIGNS_WRITE`.","tags":["Campaigns"],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","description":"Campaign name."},"campaignType":{"type":"string","description":"Binds the campaign to a type. Every article that joins it is checked automatically against the type’s requirements, and the verdict appears on the article in `potrivireCampanie`. It does not block writing — rejection stays at the order, where the payment is."},"clientName":{"type":"string","description":"The end client’s label. Free text: clients have no account on the platform."},"objective":{"type":"string","description":"What the campaign is for."},"notes":{"type":"string","description":"Internal notes."},"startsAt":{"type":"string","format":"date-time","description":"ISO 8601. Must precede `endsAt`."},"endsAt":{"type":"string","format":"date-time","description":"ISO 8601."},"budgetCents":{"type":"integer","description":"A tracked budget, in minor units. Not a separate wallet: the money stays in the organisation’s account."},"budgetCredits":{"type":"integer","description":"Limit on credits spent in the campaign."},"allowedSiteIds":{"type":"string","description":"Allowed sites. Empty means “any in the catalogue”."},"idempotencyKey":{"type":"string","description":"Chosen by you — a locally generated UUID is enough. Sent again, for the same organisation, it returns the campaign already created instead of making a new one. Recommended for any code that might resend the request after a timeout."}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key lacks the required scope"}},"security":[{"apiKey":[]},{"oauth2":["CAMPAIGNS_WRITE"]}]}},"/partner/campaigns/{campaignId}":{"get":{"operationId":"get_campaigns_campaignId","summary":"One campaign","description":"The campaign, with its articles and publications.\n\nRequired scope: `CAMPAIGNS_READ`.","tags":["Campaigns"],"parameters":[{"name":"campaignId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key lacks the required scope"}},"security":[{"apiKey":[]},{"oauth2":["CAMPAIGNS_READ"]}]},"patch":{"operationId":"patch_campaigns_campaignId","summary":"Update a campaign","description":"Every field from creation, all optional. Send only what changes.\n\nMost often used for `campaignType`: binds an already-created campaign to a type, or clears it with `null`. Every article in the campaign is automatically rechecked against the new type — see `potrivireCampanie` on the article.\n\nRequired scope: `CAMPAIGNS_WRITE`.","tags":["Campaigns"],"parameters":[{"name":"campaignId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key lacks the required scope"}},"security":[{"apiKey":[]},{"oauth2":["CAMPAIGNS_WRITE"]}]},"delete":{"operationId":"delete_campaigns_campaignId","summary":"Delete a campaign","description":"Responds `204` on success. Refused with `409` if the campaign is active and has articles — the same rule as in the interface: a campaign with history gets archived (`PATCH` with `status: \"ARCHIVED\"`), not deleted.\n\nRequired scope: `CAMPAIGNS_WRITE`.","tags":["Campaigns"],"parameters":[{"name":"campaignId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key lacks the required scope"}},"security":[{"apiKey":[]},{"oauth2":["CAMPAIGNS_WRITE"]}]}},"/partner/balance":{"get":{"operationId":"get_balance","summary":"Available balance","description":"The packages you own, with the credit left on each, and the money balance. Check it before requesting a publication — otherwise the integration finds out from an error, after it has already built the article.\n\n`available` subtracts reservations: a publication in flight has already locked the credit, even if it has not consumed it.\n\nRequired scope: `BALANCE_READ`.","tags":["Balance and reports"],"parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key lacks the required scope"}},"security":[{"apiKey":[]},{"oauth2":["BALANCE_READ"]}]}},"/partner/reports/summary":{"get":{"operationId":"get_reports_summary","summary":"Period summary","description":"Defaults to the last 30 days. An integration that sends no range gets something useful, not the entire history.\n\nRequired scope: `REPORTS_READ`.","tags":["Balance and reports"],"parameters":[{"name":"from","in":"query","required":false,"description":"Range start.","schema":{"type":"string","format":"date"}},{"name":"to","in":"query","required":false,"description":"Range end.","schema":{"type":"string","format":"date"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key lacks the required scope"}},"security":[{"apiKey":[]},{"oauth2":["REPORTS_READ"]}]}},"/partner/reports/publications":{"get":{"operationId":"get_reports_publications","summary":"Publications in range","description":"Row by row, for reporting to the client.\n\nRequired scope: `REPORTS_READ`.","tags":["Balance and reports"],"parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key lacks the required scope"}},"security":[{"apiKey":[]},{"oauth2":["REPORTS_READ"]}]}},"/partner/audit":{"get":{"operationId":"get_audit","summary":"Audit a domain","description":"Requires no permission: it neither reads nor writes anything about any organisation, it measures a public page anyone can open in a browser.\n\n**Nothing is cached.** It is used in the \"I changed something, let me check again\" loop, and a ten-minute-old answer would look identical to a fresh one and falsely say the change had no effect.\n\nIt is also not written into the catalogue site audit: a check run against an inner page would replace the score measured on the home page there.\n\nIt takes between fifteen seconds and a minute and a half — the page is loaded twice, with Lighthouse.\n\nNo scope required.","tags":["Domain audit"],"parameters":[{"name":"url","in":"query","required":true,"description":"A domain or a page URL. Without a scheme, `https` is assumed. Private-network addresses are rejected — the audit measures public sites, and the page is loaded by Google, not by our server.","schema":{"type":"string"}},{"name":"virustotal","in":"query","required":false,"description":"`1` also submits the URL to VirusTotal (~70 antivirus engines) and waits for the verdict — another 20–40 s. Off by default: their quota is 4 requests/minute and 500/day, and the audit runs dozens of times a day during layout work. Without the parameter, `virustotal` is `null` in the response — meaning “not checked”, not “clean”.","schema":{"type":"boolean"}},{"name":"strategii","in":"query","required":false,"description":"`mobile`, `desktop` or both, comma-separated. Both by default — the simulated phone throttles the CPU fourfold, and JavaScript problems only show up there.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"siguranta":{"type":"object","additionalProperties":true,"description":"Safe Browsing. A `marcat` domain gets a red interstitial in Chrome — until that is fixed, nothing else matters."},"strategii[].scoruri":{"type":"object","additionalProperties":true,"description":"Performance, accessibility, best practices and SEO, 0–100."},"strategii[].probleme":{"type":"string","description":"The failing audits, ordered: what is broken first, then what saves the most. Each carries the Lighthouse description — which also says how to fix it — plus estimated savings and up to ten offending elements."},"teren":{"type":"object","additionalProperties":true,"description":"Core Web Vitals from real visits over the last 28 days. They beat any lab score, when present."},"virustotal":{"type":"string","enum":["object","null"],"description":"Only with `virustotal=1`. `stats` counts engines per verdict, `marcatDe` lists those that flagged the URL, `proaspata` says whether the analysis is fresh or the last available one, `link` opens the full report."},"searchConsole":{"type":"object","additionalProperties":true,"description":"Indexing, robots.txt, last crawl, the canonical Google picked, mobile-usability and structured-data problems, sitemaps with their errors. Absent, with a reason, when the domain is not shared with us."}}}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key lacks the required scope"}},"security":[{"apiKey":[]},{"oauth2":[]}]}}},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"API key from Integrations (bk_live_…)"},"oauth2":{"type":"oauth2","description":"OAuth 2.1 with PKCE (S256). Dynamic client registration at https://app.comunicate.top/api/v1/oauth/register. The access token is an API key and is accepted everywhere an API key is.","flows":{"authorizationCode":{"authorizationUrl":"https://app.comunicate.top/api/v1/oauth/authorize","tokenUrl":"https://app.comunicate.top/api/v1/oauth/token","refreshUrl":"https://app.comunicate.top/api/v1/oauth/token","scopes":{"CATALOG_READ":"catalog read","ARTICLES_READ":"articles read","ARTICLES_WRITE":"articles write","MEDIA_WRITE":"media write","PUBLICATIONS_READ":"publications read","PUBLICATIONS_WRITE":"publications write","CAMPAIGNS_READ":"campaigns read","CAMPAIGNS_WRITE":"campaigns write","BALANCE_READ":"balance read","REPORTS_READ":"reports read"}}}}}}}