How to use templateDeployV2 API?
kwyszyns
PROOP

12 days ago

Hi, I have manually created a template in my project which I would now like to use to setup new instances using GraphQL API call - templateDeployV2. Unfortunately, I can't find any well documented source describing what the expected payload for this call is. I managed to find some basic schema in your graphiQL but SerializedTemplateConfig type is empty 🙁 I also tried checking the payload in Network tab during manual attempt, but it seemed quite complex and contained a lot of fields which I hope can be omitted when using your public API. In perfect scenario, I'd expect that I should only specify the environment variables necessary for each service, the same way it is done manually when clicking through the UI. Could you please guide me on how to use this API call properly? It would be nice if you could showcase a minimal payload containing all the required fields.

Solved$40 Bounty

32 Replies

kwyszyns
PROOP

12 days ago

By the way, my goal is to find the easiest way to create a set of services using API. If there is any other way that can make it easier, that'd be more than appreciated.


If you are familiar with Typescript you could try using an SDK I created. Should be easier to interface with the API this way
https://github.com/crisog/railway-sdk


brody
EMPLOYEE

12 days ago

No way, does it actually support template deploying?


kwyszyns
PROOP

12 days ago

Thanks Cris, I'll check it out!



brody
EMPLOYEE

12 days ago

Omg that's beautiful


Thanks! Will keep iterating on it <:salute:1137099685417451530>



kwyszyns
PROOP

11 days ago

Hello @CrisOG , thanks a lot for your solution. I'm currently trying to apply it using my template and it seems that I'm close, however I'm facing the following issues:

  • when trying to deploy template to an existing project and environment (I'm passing environmentId and projectId as in your example) I receive a success response. However, when I enter the project in Railway, I can't see any progress. The project is empty. When I leave the project and see the workspace page there is "1 service" label under my project name. But when I enter the project there is nothing inside.

  • when trying to deploy template as a new project in my workspace (by passing workspaceId instead of projectId and environmentId) I receive a "Not Authorized" error. I've created a workspace-level API token and used it during railway initialization:

    const railway = createRailway({
    token: process.env.WORKSPACE_TOKEN,
    tokenType: 'team',
    });

Do you have any ideas how I can solve those issues?


kwyszyns
PROOP

11 days ago

btw. your typegen worked very well. However, I had to modify it a bit because it didn't set defaultValue:string for variables where I had no default values specified. So I ran into TS issues when I was trying to set a defaultValue for those variables.


kwyszyns
PROOP

11 days ago

Another question - are you sure that the name of the variable value field should be defaultValue? In Network tab when I sent the request each variable had a value field.


I made up this helper script too quickly so I'm not 100% sure. Can you share which template you are trying to deploy? I will give it a shot and report back


I just confirmed it works with both defaultValue and value, I'll adjust the script so it sends value which seems to be the one we should be using.

I'm also merging a fix for this

it didn't set defaultValue:string for variables where I had no default values specified

Thanks for your feedback!


About this behavior, it's so weird. The exact same script with both account or workspace tokens works for me, are you still experiencing that problem?

1443583446919414000


I can confirm it does not work with

const railway = createRailway({
      token: process.env.WORKSPACE_TOKEN,
      tokenType: 'team',
    });

But it does if you remove tokenType with a WORKSPACE_TOKEN

I need to confirm if the docs are outdated regarding this. There's a high chance since teams don't exist (its workspaces now)
https://docs.railway.com/guides/public-api#using-a-team-token


kwyszyns
PROOP

11 days ago

Thank you Cris, I’ll try without tokenType soon and I’ll let you know if it worked🙌🏻


kwyszyns
PROOP

11 days ago

I'm also merging a fix for this
it didn't set defaultValue:string for variables where I had no default values specified


kwyszyns
PROOP

11 days ago

> I'm also merging a fix for this
> it didn't set defaultValue:string for variables where I had no default values specified

unfortunately didn't work for me. As a workaround I've created a normalizeConfig() function to make it work:

const normalizeConfig = (config: unknown): unknown => {
if (!config || typeof config !== 'object') {
return config;
}

const configObj = config as Record;

// Handle services with variables
if (configObj.services && typeof configObj.services === 'object') {
const services = configObj.services as Record;
const normalizedServices: Record = {};

for (const [serviceId, service] of Object.entries(services)) {
  if (!service || typeof service !== 'object') {
    normalizedServices[serviceId] = service;
    continue;
  }

  const serviceObj = service as Record;
  const normalizedService: Record = { ...serviceObj };

  // Normalize variables - add value: '' if not present
  if (serviceObj.variables &amp;&amp; typeof serviceObj.variables === 'object') {
    const variables = serviceObj.variables as Record;
    const normalizedVariables: Record = {};

    for (const [varName, varConfig] of Object.entries(variables)) {
      if (!varConfig || typeof varConfig !== 'object') {
        normalizedVariables[varName] = varConfig;
        continue;
      }

      const varConfigObj = varConfig as Record;
      normalizedVariables[varName] = {
        ...varConfigObj,
        // Add value: '' if it doesn't exist
        value: varConfigObj.value ?? '',
      };
    }

    normalizedService.variables = normalizedVariables;
  }

  normalizedServices[serviceId] = normalizedService;
}

return {
  ...configObj,
  services: normalizedServices,
};

}

return config;
};


kwyszyns
PROOP

11 days ago

> I'm also merging a fix for this
> it didn't set defaultValue:string for variables where I had no default values specified

unfortunately didn't work for me. As a workaround I've created a normalizeConfig() function to make it work:

`const normalizeConfig = (config: unknown): unknown => {
if (!config || typeof config !== 'object') {
return config;
}

const configObj = config as Record;

// Handle services with variables
if (configObj.services && typeof configObj.services === 'object') {
const services = configObj.services as Record;
const normalizedServices: Record = {};

for (const [serviceId, service] of Object.entries(services)) {
  if (!service || typeof service !== 'object') {
    normalizedServices[serviceId] = service;
    continue;
  }

  const serviceObj = service as Record;
  const normalizedService: Record = { ...serviceObj };

  // Normalize variables - add value: '' if not present
  if (serviceObj.variables &amp;&amp; typeof serviceObj.variables === 'object') {
    const variables = serviceObj.variables as Record;
    const normalizedVariables: Record = {};

    for (const [varName, varConfig] of Object.entries(variables)) {
      if (!varConfig || typeof varConfig !== 'object') {
        normalizedVariables[varName] = varConfig;
        continue;
      }

      const varConfigObj = varConfig as Record;
      normalizedVariables[varName] = {
        ...varConfigObj,
        // Add value: '' if it doesn't exist
        value: varConfigObj.value ?? '',
      };
    }

    normalizedService.variables = normalizedVariables;
  }

  normalizedServices[serviceId] = normalizedService;
}

return {
  ...configObj,
  services: normalizedServices,
};

}

return config;
};`


kwyszyns
PROOP

11 days ago

after changing defaultValue to value it worked like a charm! I've managed to deploy a new project using API and I can see it in Railway. Thanks a lot for your support 🙂


So happy to hear!


Just to confirm, is Team-Access-Token header deprecated? I was able to make a projects.list call using the same workspace token with Bearer header but not Team-Access-Token

I do see it on the docs here,
https://docs.railway.com/guides/public-api#using-a-team-token

But not here,
https://docs.railway.com/reference/public-api#team-token-and-personal-token


brody
EMPLOYEE

11 days ago

I don't see it used in the codebase anywhere


kwyszyns
PROOP

11 days ago

Hey @CrisOG , is there a possibility to retrieve public networking domains for different services using your SDK? I've examined multiple namespaces, including services, domains and networking. Networking seems to be the right direction, but currently I can't see a possibility to query public networks 🙁 The available options are tcpProxies, nodes, egressGateways.

My goal: after creating a set of services from template, I'd like to:

a) retrieve a list of public domains that the services were deployed to
b) retrieve a deployment status since it can take some time (a general status for all services or separate status for each service)


Will take a look


kwyszyns
PROOP

11 days ago

thanks!


Bump the SDK to 1.2.0, here's a sample script:

import { createRailway } from '@crisog/railway-sdk';

async function main() {
  const token = process.env['RAILWAY_API_TOKEN'];

  if (!token) {
    throw new Error('RAILWAY_API_TOKEN environment variable is required');
  }

  const railway = createRailway({
    token,
  });

  const projectId = '893d8a85-b211-4945-acc9-35bebaa07b06';
  const environmentId = 'c79d8ca3-f2b3-46f9-a5ba-3f2fe048867b';
  const serviceId = '8cdf7de4-a9f4-4693-9f84-edd0b52fbbb3';

  console.log('1. Getting public domains for service...');
  const domainsResult = await railway.domains.list({
    variables: { projectId, environmentId, serviceId },
  });

  if (domainsResult.isErr()) {
    console.error('❌ Failed to get domains:', domainsResult.error.message);
    return;
  }

  const { domains } = domainsResult.value;

  if (domains.serviceDomains.length > 0) {
    console.log('   📡 Service Domains (Railway-generated):');
    for (const d of domains.serviceDomains) {
      console.log(`      - https://${d.domain}`);
    }
  }

  if (domains.customDomains.length > 0) {
    console.log('   🌐 Custom Domains:');
    for (const d of domains.customDomains) {
      console.log(`      - https://${d.domain}`);
    }
  }

  console.log('\n2. Getting deployment status...');
  const deploymentsResult = await railway.deployments.list({
    variables: {
      input: { projectId, environmentId, serviceId },
    },
  });

  if (deploymentsResult.isErr()) {
    console.error('❌ Failed to list deployments:', deploymentsResult.error.message);
    return;
  }

  const deployments = deploymentsResult.value.deployments;
  console.log(`   Found ${deployments.length} deployments`);

  const latest = deployments[0];
  if (latest) {
    console.log(`   Latest: ${latest.status} (${latest.createdAt})`);
  }

  console.log('\n✅ Done!');
}

main().catch((error) => {
  console.error('Error:', error);
  process.exit(1);
});

Let me know if this works for you <:salute:1137099685417451530>


kwyszyns
PROOP

11 days ago

Thank you, will give it a try soon and I'll let you know <:salute:1137099685417451530> Really appreciate your work!


kwyszyns
PROOP

10 days ago

Hello @CrisOG , unfortunately I'm experiencing an authorization problem when trying to call both API calls (deployments.list and domains.list):

❌ Failed to get domains: Not Authorized


Got a notification, all good?


kwyszyns
PROOP

10 days ago

yeah, I'm currently working on it but it seems that everything works as expected. Thanks a lot 🙂


Status changed to Solved brody 10 days ago


Loading...