Skip to content

Conversation

@nytamin
Copy link
Collaborator

@nytamin nytamin commented Jun 13, 2025

This PR tracks the work being done to define the Server API portion of the OGraf specification.

Time plan

(Revised september 2025)

  • Summer 2025: Work on an initial draft within the HTML Graphics Working Group
  • Autumn 2025: First public draft announced & feedback from the industry / vendors.
  • Q1 2026: Finalize first version of the Server API (i.e. merge this PR)

Info

The Server API is an OpenApi definition, located at v1/specification/open-api/server-api.yaml
(also see the auto-generated documentation here).

Contributions & testing

Ways to contribute:

I (@nytamin ) intend to update SuperFly's "Simple Rendering System" continously throughout the discussions and keep it up-to-date with the latest server API, for testing and validation purposes.

nytamin added 13 commits May 20, 2025 14:36
not emantically correct, but technically possible
# Conflicts:
#	v1-draft-0/examples/l3rd-name/manifest.json
#	v1/examples/l3rd-name/l3rd-manifest.json
#	v1/examples/l3rd-name/manifest.json
#	v1/specification/docs/Specification.md
#	v1/typescript-definitions/package-lock.json
#	v1/typescript-definitions/package.json
# Conflicts:
#	v1/typescript-definitions/src/apis/graphicsAPI.ts
@nytamin nytamin mentioned this pull request Sep 22, 2025
@nytamin
Copy link
Collaborator Author

nytamin commented Oct 2, 2025

Notes from Working Group meeting 2025-10-01:

There was a discussion regarding PR #15 and whether we should simplify the API to only allow 1 graphic per RenderTarget.
There are both pros and cons with allowing only 1 Graphic per RenderTarget:

  • pro: This simplifies the API, since you wouldn't have to address a Graphic individually so send commands to it, only the RenderTarget (like how CasparCG works, adressing commands to a "layer").
  • con: It adds restrictions on how a Renderer must operate.
    It was noted that Ross has a way of transitioning (dissolve) between gfx, which would imply temporarily having multiple Graphics at the same time.
  • con: If we have GraphicInstanceIds, it allows controllers to use a Render "without RenderTargets", thus only adressing the Graphics themselves.

No conclusion was reached, we'll discuss this again at next meeting in two weeks.

@nytamin nytamin self-assigned this Oct 2, 2025
@dvdeurse
Copy link
Collaborator

dvdeurse commented Oct 7, 2025

I went through the OpenAPI definition and had the following remarks:

  • general
    • the Error response schema now consists of 2 fields (error and stack). Shouldn't we look into a more standardized way for representing errors (e.g. https://www.rfc-editor.org/rfc/rfc7807.html)?
    • date(time) fields are currently represented by a Unix timestamp. I believe UTC-based ISO 8601 strings are more common in today's public JSON-based HTTP APIs. They are much more readable and there is plenty of support in the libraries and frameworks.
  • Server information endpoint (GET /)
    • I would omit uptime, it has nothing to do with OGraf
  • Graphic Instance model
    • there is now a distinction between a Graphic and a Manifest, where the Graphic is a subset of the information available in the Manifest (except for the created and modified fields). Wouldn't it be cleaner to just have a full version of the Graphic (which corresponds to the content of the Manifest) and a light version (which is a subset of the Manifest) to be used in the list endpoint?
    • I'm not sure about the created and modified fields. I understand this kind of information is meaningful for clients, but it limits the way you can represent the provenance information, mostly because of the existing author field. There is now no way to distinguish between the original creator and someone else who did an update. Maybe we need a field for the graphic (which is exactly the field of the previous point) and a field for the provenance which then would contain the fields createdAt, createdBy, updatedAt, updatedBy. This way, the author field in the manifest can refer to an organization or an individual, while the provenance fields can refer to individual users.
  • Renderer (GET /renderers/{rendererId})
    • shouldn't we split the status field into status and renderTargets?
  • Renderer target (GET /renderers/{rendererId}/target)
    • why is there a surrounding renderTarget element in the response?
    • I'm not sure a target needs a status and statusMessage ... Any valid use cases you can think of?
  • Graphic actions (play/update/stop/custom/gotoTime/setActionSchedule)
    • depending on the GraphicInstanceId vs GraphicId discussion for the identification inside a target, but if we choose the GraphicInstanceId path, I wonder whether we still need the renderTarget to address a graphic instance (given we already have the GraphicInstanceId). Of course, this would mean that the GraphicInstanceId is unique across a Renderer, not only across a target.
    • I would also add the Graphic identification (whether it will be GraphicsInstanceId, RendererTarget, GraphicsId or a combination) into the body, instead of using query params
    • if we would go for GraphicsInstanceId identification only, I would also propose to change the URI to /renderers/{rendererId}/graphic (so omitting the 'target' part), as in this case, you execute an action on a Graphic, not on a target. This approach would allow you to explain both Renderers that don't care about targets and Renderers that do.

@nytamin
Copy link
Collaborator Author

nytamin commented Oct 29, 2025

Notes from Working Group meeting 2025-10-29:

  • Discussion on whether to have one or multiple graphics per renderTarget. We concluded with that since we have no definite arguments for or against, we'll go with the generic approach for now (ie multiple graphics per RenderTarget).
  • It might be useful if a Renderer can specify whether it renders one or multiple graphics per renderTarget.
  • Davy's proposals look good!

# Conflicts:
#	v1/typescript-definitions/package-lock.json
#	v1/typescript-definitions/package.json
#	v1/typescript-definitions/src/main.ts
# Conflicts:
#	.github/workflows/publish-npm.yml
ServerAPI: Remove GraphicId in favor for GraphicInstanceId
@nytamin
Copy link
Collaborator Author

nytamin commented Nov 12, 2025

Notes from Working Group 2025-11-12:

Idea: It should be possible to clear a whole renderer, not just graphics on a single renderTarget

@nytamin
Copy link
Collaborator Author

nytamin commented Nov 12, 2025

I pushed a few modifications to this PR based on @dvdeurse 's suggestions.

Notable exceptions:

Wouldn't it be cleaner to just have a full version of the Graphic (which corresponds to the content of the Manifest) and a light version (which is a subset of the Manifest) to be used in the list endpoint?

I couldn't come up with a good solution for this. Let's discuss it some time.

I wonder whether we still need the renderTarget to address a graphic instance (given we already have the GraphicInstanceId). Of course, this would mean that the GraphicInstanceId is unique across a Renderer, not only across a target.

I think it would be useful to keep the graphicsInstanceId being unique only within its RenderTarget. This way we allow for a Renderer that doesn't really support instances of a graphic to function, by just returning the graphicId as graphicInstanceId upon load.

@dvdeurse
Copy link
Collaborator

Thx Johan for these modifications. Some remarks:

Wouldn't it be cleaner to just have a full version of the Graphic (which corresponds to the content of the Manifest) and a light version (which is a subset of the Manifest) to be used in the list endpoint?

I couldn't come up with a good solution for this. Let's discuss it some time.

In the end, for a single Graphic, there are two parts:

  • information from the manifest
  • information from the server, for example when a Graphic was uploaded (createdAt), by whom (createdBy) etc. Typically provenance information, but maybe there are other use cases. The idea is that you can upload a Graphic to multiple servers. The manifest part will always be the same, but the additional server-specific part will differ. One approach could also be that we omit this part from the spec and leave that up to the vendors.

Then there's the info from the manifest itself. Do we want a lightweight form of it? I believe we do, for two reasons:

  • keep the list endpoint light
  • when referencing a Graphic (e.g. in the list of Graphic Instances loaded in a target), you don't want to provide the full manifest

Therefore, I propose the following data model for a Graphic, where server is to be discussed whether we want this in the spec or not (and if yes, the name of the field server might be improved as well):

{
  "graphic": {
    "$schema": "string",
    "id": "string",
    "version": "string",
    "main": "string",
    "name": "string",
    "description": "string",
    "author": {
      "name": "string",
      "email": "string",
      "url": "string"
    },
    "customActions": [
      "string"
    ],
    "supportsRealTime": true,
    "supportsNonRealTime": true,
    "stepCount": 1,
    "schema": {
    },
    "renderRequirements": [
      {
        "resolution": {
          "width": "string",
          "height": "string"
        },
        "frameRate": "string",
        "accessToPublicInternet": "string"
      }
    ]
  },
  "server": {
    "createdAt": "...",
    "createdBy: {
      ...
    }
  }
}

That is the full version, the lightweight version of a Graphic would then be:

{
  "id": "string",
  "name": "string",
  "description": "string"
}

I think it would be useful to keep the graphicsInstanceId being unique only within its RenderTarget. This way we allow for a Renderer that doesn't really support instances of a graphic to function, by just returning the graphicId as graphicInstanceId upon load.

Agreed.


On the clearGraphic endpoint, there is an error in the request data model. I believe you intended to implement an array of filters, but the schema currently models filters as an object.


Finally, there was one remark that you forgot to implement or mention:

I would also add the Graphic identification (whether it will be GraphicsInstanceId, RendererTarget, GraphicsId or a combination) into the body, instead of using query params

This remark was about the query params on the following endpoints:

  • clearGraphic
  • loadGraphic
  • updateAction
  • playAction
  • customAction
  • gotoTime

I don't see a reason why they should be query params instead of being part of the request body. The only place where the query param makes sense is on the GET /renderers/{rendererId}/target (as you cannot provide a body in a GET request).

@nytamin
Copy link
Collaborator Author

nytamin commented Nov 19, 2025

Good suggestions @dvdeurse !

I've updated the schema to what you proposed:

  • I chose the name metadata instead of server for the graphics endpoint. I think it fits better, but I'm open for further suggestions!
  • I changed the filters on the clearGraphics endpoint, good spot!
  • I moved the renderTarget and graphicInstanceId from query to the body, where applicable.

@nytamin
Copy link
Collaborator Author

nytamin commented Nov 21, 2025

Here are a few more suggestions for changes:

  • Use POST for load:
    The endpoint /renderers/{rendererId}/target/graphic/load currently uses PUT to load (create) a new GraphicInstance. Since the client does not provide the graphicInstanceId and the server generates it, POST is the standard HTTP method for this operation. PUT implies idempotency and usually requires the client to specify the full URI or resource ID.
  • Renderer vs Graphic Custom Actions:
    • Renderer custom actions have their own path: /renderers/{rendererId}/customActions/{customActionId}
    • Graphic custom actions use a generic endpoint: /renderers/{rendererId}/target/graphic/customAction with the action ID in the body.
    • This asymmetry is slightly confusing. It might be cleaner to have /renderers/{rendererId}/target/graphic/{instanceId}/customActions/{actionId} to align with the Renderer pattern and standard REST sub-resource patterns.
  • Graphic Instance Resource: The API structure /renderers/{rendererId}/target/graphic/... treats "graphic" as a singular controller for potentially multiple instances (via body params). A more RESTful approach would be to expose graphic instances as addressable resources:
    • POST /renderers/{rendererId}/target/graphics (load)
    • GET /renderers/{rendererId}/target/graphics/{instanceId}
    • DELETE /renderers/{rendererId}/target/graphics/{instanceId}
    • POST /renderers/{rendererId}/target/graphics/{instanceId}/play
    • POST /renderers/{rendererId}/target/graphics/{instanceId}/stop
    • PATCH /renderers/{rendererId}/target/graphics/{instanceId} (update)
  • CORS:
    If these graphics are loaded from different origins (e.g., a marketplace), Cross-Origin Resource Sharing (CORS) headers and policies should be explicitly defined in the spec to ensure Renderers can load them.

(disclaimer: These where identified with the help of an AI tool)

@dvdeurse
Copy link
Collaborator

Use POST for load:
The endpoint /renderers/{rendererId}/target/graphic/load currently uses PUT to load (create) a new GraphicInstance. Since the client does not provide the graphicInstanceId and the server generates it, POST is the standard HTTP method for this operation. PUT implies idempotency and usually requires the client to specify the full URI or resource ID.

Yes, either change PUT into POST or add the graphicInstanceId to the request (so move the responsibility of generating this ID to the client). The latter would allow 'simple' controllers to keep graphicInstanceId and graphicId the same so they don't need to keep track of the 'generated' instance ids.

Renderer vs Graphic Custom Actions:
Renderer custom actions have their own path: /renderers/{rendererId}/customActions/{customActionId}
Graphic custom actions use a generic endpoint: /renderers/{rendererId}/target/graphic/customAction with the action ID in the body.
This asymmetry is slightly confusing. It might be cleaner to have /renderers/{rendererId}/target/graphic/{instanceId}/customActions/{actionId} to align with the Renderer pattern and standard REST sub-resource patterns.

Agreed.

Graphic Instance Resource: The API structure /renderers/{rendererId}/target/graphic/... treats "graphic" as a singular controller for potentially multiple instances (via body params). A more RESTful approach would be to expose graphic instances as addressable resources: ...

Yes, but then you loose the ability to play/stop multiple instances at the same time. Especially if you want these actions to happen in the same 'frame' (so in sync), the only way to do this is to combine them into one request (or to provide scheduled timestamps in the requests, but I don't think we want to go down that path). However, looking at the current API proposal, we cannot do multiple play/stop instances anyway. Isn't that a use case we want to support?

Whatever we decide here, I would like to propose to rename the graphic part in the URLs into instance or graphicInstance because that's the resource type you're dealing with in those requests.

CORS:
If these graphics are loaded from different origins (e.g., a marketplace), Cross-Origin Resource Sharing (CORS) headers and policies should be explicitly defined in the spec to ensure Renderers can load them.

Ok, something similar to https://specs.amwa.tv/is-04/releases/v1.2.2/docs/2.3._APIs_-_Server_Side_Implementation_Notes.html#cross-origin-resource-sharing-cors then?

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants