Skip to content

feat: add Actor task publication endpoints - #2840

Open
Janjiran wants to merge 5 commits into
masterfrom
feat/public-tasks-api
Open

feat: add Actor task publication endpoints#2840
Janjiran wants to merge 5 commits into
masterfrom
feat/public-tasks-api

Conversation

@Janjiran

@Janjiran Janjiran commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Part of https://github.com/apify/apify-core/issues/29471

Adding new public endpoint for Actor Task publication API.


Blocked by https://github.com/apify/apify-core/pull/29623

@Janjiran Janjiran self-assigned this Aug 2, 2026
@Janjiran Janjiran added the t-growth Issues owned by the Growth team. label Aug 2, 2026
@Janjiran Janjiran added this to the 146th sprint - Growth team milestone Aug 2, 2026
@apify-service-account

apify-service-account commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

✅ Preview for this PR (commit e39a1685) is ready at https://pr-2840.preview.docs.apify.com (see action run).

@apify-service-account

Copy link
Copy Markdown
Contributor

Important

Action required@Janjiran please coordinate this docs PR with the Python API client PR linked below.

Because this PR modifies the OpenAPI specification, the generated models in apify-client-python must be regenerated to stay in sync. This has already been done automatically:

A companion PR has been opened in apify-client-python with the regenerated models: apify/apify-client-python#985

  • Please make sure to review and merge both PRs together to keep the OpenAPI spec and API clients in sync.
  • You can ask for review and help from the Tooling team if needed.

@janbuchar janbuchar left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks legit as far as openapi semantics are considered

@fnesveda fnesveda left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the API final? Why is it so different from the Actor publication API?
For example:

  • Actors are published through the "update Actor" endpoint, tasks have special /publish and /unpublish endpoints
  • Actors have isPublic, tasks have publishedAt
  • Actors have a categories array, tasks have categorization string
  • Actors have all the details (seoTitle etc.) on the top level, tasks have it in publicConfig

Can we unify the API so that it's the same as for Actors?

Also, property names like inputSchemaFields, datasetName and datasetView somehow don't capture that they're being used only when showing the task, but they sound like they're relevant to the whole task (e.g. that they limit what input fields can the task process). How about putting them under some displayOptions object?

Also CC @gippy - would you look at this through the lens of the rules for API endpoints you wrote? 🙏

Task create now documents `publicConfig` (the API accepts the display fields and
ignores the server-controlled `publishedAt`/`renderableAt`), and the update
schema no longer offers a null the API rejects.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@gippy

gippy commented Aug 4, 2026

Copy link
Copy Markdown
Member

We already discussed this.

The 3 points:

  • Actors have isPublic, tasks have publishedAt
  • Actors have a categories array, tasks have categorization string
  • Actors have all the details (seoTitle etc.) on the top level, tasks have it in publicConfig

Already exist in the current console backend API, so changing them would require migration and complete rewrite.

I already shared my opinion on this when it was going to be impelemented in console backend, but now that train has left the station.

So for API I proposed the option to have the /publish and /unpublish endpoints, because public tasks do not have isPublic field and sending publishedAt: <some date> / publishedAt: null to publish/unpublish is weird. Same sending isPublic field which is just virtual and does not really exist. It matches what the guidelines say and it's not that uncommon.

Since publishing does multiple verification steps, not just for the task detail but also for Actor permission and the Actor "readyness" for task publication it kinda makes sense.

Alternatives are either:

  1. Have a single PUT Actor task endpoint which takes a different shape of data and then "transforms" it into real data in the database. Which is hacky
  2. Completely redoing the public tasks schema to match Actors, but then the task would have many optional fields which 99% of tasks do not have, which is also is not great.

From my point of view I think it's ok to not have it match, the logic on Actor is not great either with the Actor update endpoint having 9 dependencies and over 400+ lines of code in the updateActor function alone. This way at least the logic is pretty simple. Update task endpoint just checks if the task is valid against schema and all logic related to publication/unpublication is separate. It also makes it much easier to document the errors the endpoints returns.

@fnesveda

fnesveda commented Aug 4, 2026

Copy link
Copy Markdown
Member

Already exist in the current console backend API, so changing them would require migration and complete rewrite.

I already shared my opinion on this when it was going to be impelemented in console backend, but now that train has left the station.

I really don't agree with this. We can't shape the public API around our internal implementation, we need to make a good public API and then make the implementation work with it.

Writing a migration is a one time pain (and I believe this one can be done through Claude pretty well). A bad API stays with us forever.

So for API I proposed the option to have the /publish and /unpublish endpoints

On their own, these are good, and I agree we should have done the Actors API this way, but that train has surely left the station. So now that we have the Actors API done like this, let's try to have the tasks API consistent with it.

@Janjiran

Janjiran commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

Already exist in the current console backend API, so changing them would require migration and complete rewrite.

I already shared my opinion on this when it was going to be impelemented in console backend, but now that train has left the station.

I really don't agree with this. We can't shape the public API around our internal implementation, we need to make a good public API and then make the implementation work with it.

Writing a migration is a one time pain (and I believe this one can be done through Claude pretty well). A bad API stays with us forever.

So for API I proposed the option to have the /publish and /unpublish endpoints

On their own, these are good, and I agree we should have done the Actors API this way, but that train has surely left the station. So now that we have the Actors API done like this, let's try to have the tasks API consistent with it.

Isn't that exactly what we're trying to do here though? To make a good public API and then make the implementation work with it, instead of shaping the new API around the Actor publication design, which you agree is the worse one. So we are designing the tasks API the way we think a good API should look.

I get the consistency point, but if a bad API stays with us forever, then requiring every new API to copy the old pattern is exactly how it stays forever. By following the latest API conventions @gippy put together, the /publish and /unpublish endpoints should be the correct way and later we can add the same endpoints to Actors. That's a non-breaking change and the isPublic flow can be deprecated over time. Copying the Actor design into the tasks API now closes that door.

The schema differences (categories vs. categorization, publicConfig etc) are a fair point but I'd separate it from the publish endpoints discussion.

@fnesveda

fnesveda commented Aug 5, 2026

Copy link
Copy Markdown
Member

Isn't that exactly what we're trying to do here though? To make a good public API and then make the implementation work with it, instead of shaping the new API around the Actor publication design, which you agree is the worse one. So we are designing the tasks API the way we think a good API should look.

I get your point, and in isolation, the /publish + /unpublish flow is better, but the isPublic flow isn't so bad that it would warrant breaking the consistency across endpoints. This is how I sort the options according to how nice they are:

  1. /publish + /unpublish flow on both Actors and tasks
  2. isPublic flow on both Actors and tasks
  3. /publish + /unpublish on one, isPublic on the other

I get the consistency point, but if a bad API stays with us forever, then requiring every new API to copy the old pattern is exactly how it stays forever. By following the latest API conventions @gippy put together, the /publish and /unpublish endpoints should be the correct way and later we can add the same endpoints to Actors. That's a non-breaking change and the isPublic flow can be deprecated over time. Copying the Actor design into the tasks API now closes that door.

Requiring every new API to copy the old pattern is how we prevent a bad API in the first place - an inconsistent API is a bad API.

If we decide "later we can add the same endpoints to Actors", realistically, we'll never do it, because we'll always have better things to do than changing an ancient, heavily integrated, slightly bad API flow. (I've had my technical purist heart broken many times by the reality of prioritization with limited capacity 🥲)

Replaces the dedicated publish/unpublish endpoints with a virtual `isPublic`
field on the task update, matching how Actors are published. `isPublic` is also
returned on the task, derived from `publicConfig.publishedAt`.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Janjiran
Janjiran requested review from fnesveda and removed request for szaganek August 6, 2026 10:15
@Janjiran

Janjiran commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

Based on the discussion above, we've agreed to change the API to match the Actors:

  • publication status is changed via virtual isPublic property that end-user can send with the PUT request. The isPublic is also returned from GET and LIST endpoints and dynamically computed via publishedAt field. Other tasks I've put in a ticket https://github.com/apify/apify-core/issues/29803 and will be discussing later

@fnesveda

fnesveda commented Aug 6, 2026

Copy link
Copy Markdown
Member

I like the isPublic changes, thanks. I'll review this fully once the API is finalized. Until then, maybe it's worth converting this PR to draft so that we don't merge it accidentally?

@Janjiran
Janjiran marked this pull request as draft August 6, 2026 12:23
@Janjiran
Janjiran requested a review from szaganek August 10, 2026 12:28
@Janjiran
Janjiran marked this pull request as ready for review August 10, 2026 12:29

@katzino katzino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM ✅
Had some factual comments, feel free to skip those that doesn't make sense to you. Maybe someone from the content team could help us to formulate it properly.

Comment thread apify-api/openapi/components/schemas/actor-tasks/Task.yaml Outdated
Comment thread apify-api/openapi/components/schemas/actor-tasks/TaskPublicConfig.yaml Outdated
Comment thread apify-api/openapi/components/schemas/actor-tasks/TaskPublicConfig.yaml Outdated
- $ref: ../actors/ActorStandby.yaml
- type: "null"
publicConfig:
description: |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd go with something like this:
Configuration that controls how the published task appears on its public landing page. Editing this object requires write permission to the Actor that the task belongs to.

We don't need to include info on what not to do here, the publication process should be explained in the endpoint description and the relevant field.

format: uri
isPublic:
type: boolean
description: |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fluffy :D I'd go with:

Whether the task is published. Based on the 'publicConfig.publishedAt' field.

The rest of the description seems to belong to the endpoint description, not the field.

format: date-time
readOnly: true
description: |
Time when the task was published, or `null` if the task is not published.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Time when the task was published, or `null` if the task is not published.
Time when the task was published, or `null` if the task isn't published.

readOnly: true
description: |
Time when the task was published, or `null` if the task is not published.
This field is server-controlled - to publish or unpublish a task, set `isPublic`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shorter sentences improve readability, I'd go with:
This field is server-controlled. To publish or unpublish a task, use the [Update task](/api/v2/actor-task-put) endpoint and set 'isPublic'.

examples: ["2025-06-16T09:20:45.777Z"]
seoTitle:
type: [string, "null"]
description: SEO title of the public task page. Defaults to the task title when not set.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
description: SEO title of the public task page. Defaults to the task title when not set.
description: Name of the Actor task to display by search engines such as Google. Defaults to the task title.

Worth adding character limit or similar things.

examples: [Scrape data from a website]
seoDescription:
type: [string, "null"]
description: SEO description of the public task page. Defaults to the task description when not set.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
description: SEO description of the public task page. Defaults to the task description when not set.
description: Description of the Actor task to display by search engines such as Google. Defaults to the task description.

description: SEO description of the public task page. Defaults to the task description when not set.
categorization:
type: [string, "null"]
description: Use-case category of the public task.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
description: Use-case category of the public task.
description: Category that best defines the use case of the task.

type: [string, "null"]
description: |
Name of the dataset from the Actor's dataset schema whose results are displayed. When
`null`, the Actor's default dataset is used - the only dataset it declares, or the one

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
`null`, the Actor's default dataset is used - the only dataset it declares, or the one
`null`, the Actor's default dataset is used. That is, the only dataset the Actor declares, or the one

type: [string, "null"]
description: |
Key of the dataset view from the Actor's dataset schema used to display results. Must be
one of the views declared on the resolved dataset; a task cannot be published without it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
one of the views declared on the resolved dataset; a task cannot be published without it.
one of the views declared on the resolved dataset. You can't publish a task without it.

description: |
Public-facing display configuration of the task's public landing page. The provided
fields are merged into the stored configuration and validated. The stored configuration
cannot be cleared this way. Set `isPublic` to change the publication state.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How to clear the stored configuration then?

@fnesveda fnesveda left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see some of the things I've raised in the previous review are still here, and still different from Actors:

  • Actors have a categories array, tasks have categorization string
  • Actors have all the details (seoTitle etc.) on the top level, tasks have it in publicConfig

Also, property names like inputSchemaFields, datasetName and datasetView somehow don't capture that they're being used only when showing the task, but they sound like they're relevant to the whole task (e.g. that they limit what input fields can the task process). How about putting them under some displayOptions object?

Have you discussed them already, and if so, what was the verdict? The response shape is also a part of the public API, so we need to have it finalized before merging this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

t-growth Issues owned by the Growth team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants