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.
32 Replies
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.
12 days ago
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
12 days ago
No way, does it actually support template deploying?
12 days ago
12 days ago
Omg that's beautiful
12 days ago
Thanks! Will keep iterating on it <:salute:1137099685417451530>
12 days ago
Made a much more advanced templates example, hope it helps!
https://github.com/crisog/railway-sdk/blob/main/examples/templates/advanced/deploy.ts
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?
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.
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.
11 days ago
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
11 days ago
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!
11 days ago
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?

11 days ago
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
Thank you Cris, I’ll try without tokenType soon and I’ll let you know if it worked🙌🏻
I'm also merging a fix for this
it didn't set defaultValue:string for variables where I had no default values specified
> 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 && 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;
};
> 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 && 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;
};`
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 🙂
11 days ago
So happy to hear!
11 days ago
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
11 days ago
I don't see it used in the codebase anywhere
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)
11 days ago
Will take a look
11 days ago
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);
});11 days ago
Let me know if this works for you <:salute:1137099685417451530>
Thank you, will give it a try soon and I'll let you know <:salute:1137099685417451530> Really appreciate your work!
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
10 days ago
Got a notification, all good?
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