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; q?: string; limit?: number; cursor?: string }): Promise<{
locations: Location[]
nextCursor?: string | null
}>Prop
Type
const { locations } = await client.locations.list()
const { locations: cafes } = await client.locations.list({ q: "café" })
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. The search rules of q are on Characters: the first 5 words count, and %, _ and * are not wildcards. Send the same q with each cursor.
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 listed value the model does not take is not refused. Nodaro uses a value the model supports instead, or drops the setting when the model has none. The credits follow the value that ran, and the job's input_data shows it.
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
How do I create a location with the Nodaro SDK?
Call client.locations.create with a nodeId and a name, generate establishing shots with client.locations.generate, and approve one with client.locations.approveMainImage.
Which variants can I generate for a location?
Time of day, weather, seasons, angles and lighting, plus custom variants, with generateAsset, and atmosphere clips with generateMotion. On Nodaro Cloud, generateSurroundContinuation adds 360-degree views.
How do I avoid overwriting a location someone else changed?
Pass expectedUpdatedAt, the updatedAt value you read, to client.locations.update. When the location changed, the call fails with 409 concurrent_modification. Read it again, merge, and retry.
Can I delete a location permanently with the SDK?
No. delete archives the location, and restore brings it back. Permanent deletion is available only in the Nodaro app.
Related
Locations
Location Asset
Locations
Characters
Objects and creatures
Last updated on
Characters
Create, search and train characters, generate portrait candidates, approve one, and add expressions, poses and motion clips 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.