Locations
Create locations, generate establishing shots, approve one, and add time-of-day, weather, season, angle and motion variants from TypeScript.
client.locations scripts everything Location Studio does: it creates and edits locations, generates establishing-shot candidates, approves one as the main image, and adds variants for time of day, weather, seasons, angles and lighting, plus atmosphere clips. A location keeps its main image, its variant collections, its reference photos and a caption, so every later shot can show the same place. The methods call the Locations REST API. See Locations for the editor view.
Methods
| Method | What it does |
|---|---|
list(params?) | List your locations |
listArchived(params?) | List your archived locations |
get(id) | Read one location, with its jobs in progress |
create(input) | Create a location |
update(id, input) | Change a location |
delete(id) and restore(id) | Archive a location, or bring it back |
generate(input) | Generate establishing-shot candidates |
generateAsset(input) | Generate a time-of-day, weather, season, angle or lighting variant |
generateSurroundContinuation(input) | Generate the next view of a 360-degree ring |
generateMotion(input) | Animate the main image into an atmosphere clip |
removeAsset(id, data) | Remove one take from a variant collection |
approveMainImage(id, candidateJobId) | Make a candidate the main image |
recaption(id) | Write the location's description again |
client.locations
list(params?)
Lists your locations. By default it returns active locations only. Paging is optional: without limit you get the whole list and no cursor; with limit, at most 500, you get one page and a nextCursor.
list(params?: { archived?: boolean; limit?: number; cursor?: string }): Promise<{
locations: Location[]
nextCursor?: string | null
}>Prop
Type
const { locations } = await client.locations.list()
const page = await client.locations.list({ limit: 100 })
const next = await client.locations.list({ limit: 100, cursor: page.nextCursor ?? undefined })Keep paging until nextCursor is null.
listArchived(params?)
Lists your archived locations. It is a shortcut for list({ archived: true }).
listArchived(params?: { limit?: number; cursor?: string }): Promise<{ locations: Location[]; nextCursor?: string | null }>Prop
Type
const { locations: archived } = await client.locations.listArchived()get(id)
Reads one location, with pendingJobs, the variants still generating, and previousCandidates, up to 5 earlier main-image candidates, newest first. Promote one of those with approveMainImage().
get(id: string): Promise<LocationDetail>Prop
Type
const location = await client.locations.get(locationId)
console.log(location.sourceImageUrl, location.weather, location.atmosphereMotions)An archived location is still returned by id, so workflow nodes that point at it keep loading. A Location has its main image in sourceImageUrl, six collections (timeOfDay, weather, seasons, angles, lighting and atmosphereMotions, each a list of { name, url }), boards, referencePhotos, canonicalDescription, styleLock and updatedAt.
create(input)
Creates a location. name and nodeId are required. A script without a canvas node can pass "mcp-managed" as nodeId.
create(input: CreateLocationInput): Promise<{ id: string }>Prop
Type
const { id: locationId } = await client.locations.create({
nodeId: "mcp-managed",
name: "Rainy Tokyo Alley",
description: "Neon-soaked alley with vending machines",
category: "urban",
style: "realistic",
})update(id, input)
Changes a location. Only the fields you send are written. The variant collections are not part of this call, because generation jobs add to them while you work. Use generateAsset() and removeAsset() for them.
update(id: string, input: UpdateLocationInput): Promise<{ id: string; updatedAt: string }>Prop
Type
await client.locations.update(locationId, {
canonicalDescription: "A narrow, rain-soaked alley lit by neon signs",
styleLock: false,
piiConsentAt: new Date().toISOString(),
expectedUpdatedAt: location.updatedAt,
})The 409 concurrent_modification arrives as a plain NodaroError with that code. Read the location again, merge your change, and retry.
delete(id) and restore(id)
delete() archives a location, and restore() brings it back. Permanent deletion is only available in the Nodaro app. When a restored name matches an active location's name, ignoring case, the server adds (restored) and returns the name it used.
delete(id: string): Promise<{ success: true; archived: true }>
restore(id: string): Promise<{ id: string; name: string }>Prop
Type
await client.locations.delete(locationId)
const { name } = await client.locations.restore(locationId)generate(input)
Generates establishing-shot candidates (POST /v1/generate-location). With count above 1, every job is reserved before any starts, so a failure part way through rolls the batch back.
generate(input: GenerateLocationInput): Promise<{ jobIds: string[]; jobId?: string }>Prop
Type
// One candidate, written to the location when it completes
const { jobIds: [jobId] } = await client.locations.generate({
name: "Rainy Tokyo Alley",
description: "Neon-soaked alley with vending machines",
attachToLocationId: locationId,
})
// Four candidates to choose from
const { jobIds } = await client.locations.generate({ name: "Rainy Tokyo Alley", count: 4 })With attachToLocationId and count of 1, the result becomes the main image when the job completes. Otherwise, pick a candidate with approveMainImage(). quality and resolution are priced as in Generate Image. A value the model does not support is ignored.
generateAsset(input)
Generates one variant of the location (POST /v1/generate-location-asset). With attachToLocationId, attachToColumn and attachName, the result is added to that collection when the job completes.
generateAsset(input: GenerateLocationAssetInput): Promise<{ jobId: string }>Prop
Type
const { jobId } = await client.locations.generateAsset({
name: "Rainy Tokyo Alley",
assetType: "weather",
variant: "storm",
attachToLocationId: locationId,
attachToColumn: "weather",
attachName: "storm",
})generateSurroundContinuation(input)
Generates the next view of a 360-degree ring (POST /v1/generate-surround-continuation), continuing from the previous view. The server keeps half of the previous view exactly as it is, paints the other half, and matches its colors, so neighboring views join without a seam. This method works on Nodaro Cloud; other editions answer 403 edition_required before any processing, which the SDK throws as ForbiddenError.
generateSurroundContinuation(input: GenerateSurroundContinuationInput): Promise<{ jobId: string }>Prop
Type
const { jobId } = await client.locations.generateSurroundContinuation({
referenceImageUrl: previousRingView,
direction: "right",
degrees: 45,
provider: "nano-banana-pro",
aspectRatio: "16:9",
attachToLocationId: locationId,
attachToColumn: "angles",
attachName: "Surround 45°",
})generateMotion(input)
Animates the location's image into an atmosphere clip (POST /v1/generate-location-motion), with Generate Video in image-to-video mode. A location has one motion collection, so the clip always goes to atmosphereMotions and there is no attachToColumn.
generateMotion(input: GenerateLocationMotionInput): Promise<{ jobId: string }>Prop
Type
const { jobId } = await client.locations.generateMotion({
name: "Rainy Tokyo Alley",
motionPrompt: "Slow dolly-in, neon signs flicker, light rain falling",
sourceImageUrl: location.sourceImageUrl!,
provider: "kling",
attachToLocationId: locationId,
attachName: "neon dolly-in",
})removeAsset(id, data)
Removes one take from a variant collection (POST /v1/locations/:id/remove-asset). Every entry with that url is removed in one step. Use it, for example, before you regenerate a 360-degree view.
removeAsset(id: string, data: { column: LocationAttachColumn; url: string }): Promise<{ removed: true }>Prop
Type
await client.locations.removeAsset(locationId, { column: "angles", url: oldViewUrl })Throws NotFoundError when the URL is not in that collection or the location is not yours.
approveMainImage(id, candidateJobId)
Makes a completed candidate from generate() the location's main image. A vision model then writes the location's description, and the method returns both.
approveMainImage(id: string, candidateJobId: string): Promise<{ sourceImageUrl: string; canonicalDescription: string | null }>Prop
Type
const { sourceImageUrl, canonicalDescription } = await client.locations.approveMainImage(locationId, jobIds[0])canonicalDescription is null when the description could not be written. The main image is still set; call recaption() to try again.
recaption(id)
Writes the location's description again from its current main image.
recaption(id: string): Promise<{ canonicalDescription: string }>Prop
Type
const { canonicalDescription } = await client.locations.recaption(locationId)Fails with 400 no_source_image when the location has no main image, and with a 502 when the vision model fails.
Frequently asked questions
Related
Locations
Location Asset
Locations
Characters
Objects and creatures
Last updated on
Characters
Create characters, generate portrait candidates, approve one, and add expressions, poses and motion clips from TypeScript with client.characters.
Objects and creatures
Create objects and creatures, generate main images and variants, animate them, and make a creature talk from TypeScript with the Nodaro SDK.