Index: frontend/src/pages/PublishSong.tsx
===================================================================
--- frontend/src/pages/PublishSong.tsx	(revision 92db381e21eb0f44de317188e162a4362d50faf0)
+++ frontend/src/pages/PublishSong.tsx	(revision db5fb23a9c7d281ecb48c2ebf32e7ff4d9a24c38)
@@ -23,5 +23,5 @@
 	const [genre, setGenre] = useState("");
 	const [link, setLink] = useState("");
-	const [_coverFile, setCoverFile] = useState<File | null>(null); // TODO: Use in API call
+	const [coverFile, setCoverFile] = useState<File | null>(null);
 	const [coverPreview, setCoverPreview] = useState<string | null>(null);
 	const [contributors, setContributors] = useState<Contributor[]>([]);
@@ -93,5 +93,4 @@
 				`/users/search?type=ARTIST&q=${encodeURIComponent(query)}&limit=5`,
 			);
-
 			const filtered = response.data.filter(
 				(artist: ArtistSearchResult) =>
@@ -129,5 +128,5 @@
 		}
 
-		if (contributors.some((c) => c.username === artist.username)) {
+		if (contributors.some((c) => c.id === artist.id)) {
 			toast.error("This contributor is already added");
 			return;
@@ -137,5 +136,5 @@
 			...contributors,
 			{
-				username: artist.username,
+				id: artist.id,
 				fullName: artist.fullName,
 				role: selectedContributorRole,
@@ -148,6 +147,6 @@
 
 	// Remove contributor
-	const handleRemoveContributor = (username: string) => {
-		setContributors(contributors.filter((c) => c.username !== username));
+	const handleRemoveContributor = (id: number) => {
+		setContributors(contributors.filter((c) => c.id !== id));
 	};
 
@@ -242,5 +241,5 @@
 		}
 
-		if (song.contributors.some((c) => c.username === artist.username)) {
+		if (song.contributors.some((c) => c.id === artist.id)) {
 			toast.error("This contributor is already added");
 			return;
@@ -249,5 +248,5 @@
 		const role = albumSongSelectedRole[songIndex] || ROLE_OPTIONS[0].value;
 		const newContributor: Contributor = {
-			username: artist.username,
+			id: artist.id,
 			fullName: artist.fullName,
 			role,
@@ -263,21 +262,16 @@
 	};
 
-	const handleRemoveAlbumSongContributor = (
-		songIndex: number,
-		username: string,
-	) => {
+	const handleRemoveAlbumSongContributor = (songIndex: number, id: number) => {
 		const song = albumSongs[songIndex];
 		handleAlbumSongChange(
 			songIndex,
 			"contributors",
-			song.contributors.filter((c) => c.username !== username),
+			song.contributors.filter((c) => c.id !== id),
 		);
 	};
 
-	// Form submission
 	const handleSubmit = async (e: React.FormEvent) => {
 		e.preventDefault();
 
-		// Validation
 		if (!title.trim()) {
 			toast.error("Please enter a title");
@@ -295,5 +289,4 @@
 			}
 		} else {
-			// Album validation
 			for (let i = 0; i < albumSongs.length; i++) {
 				if (!albumSongs[i].title.trim()) {
@@ -311,13 +304,53 @@
 
 		try {
-			// TODO: replace with API call
-			// const formData = new FormData();
-			// formData.append('title', title);
-			// formData.append('genre', genre);
-			// if (coverFile) formData.append('cover', coverFile);
-			// ...
-
-			// Simulate API call
-			await new Promise((resolve) => setTimeout(resolve, 1000));
+			const formData = new FormData();
+			formData.append("title", title);
+			formData.append("genre", genre);
+			if (coverFile) formData.append("cover", coverFile);
+
+			// Helper to append contributors in indexed format for Spring @ModelAttribute
+			const appendContributors = (
+				formData: FormData,
+				contributors: Contributor[],
+				prefix: string,
+			) => {
+				contributors.forEach((c, i) => {
+					formData.append(`${prefix}[${i}].id`, c.id.toString());
+					formData.append(`${prefix}[${i}].artistName`, c.fullName);
+					formData.append(`${prefix}[${i}].role`, c.role);
+				});
+			};
+
+			if (releaseType === "album") {
+				// Append album songs in indexed format
+				albumSongs.forEach((song, songIndex) => {
+					formData.append(`albumSongs[${songIndex}].title`, song.title);
+					formData.append(`albumSongs[${songIndex}].link`, song.link);
+					// Append nested contributors for each song
+					song.contributors.forEach((c, contribIndex) => {
+						formData.append(
+							`albumSongs[${songIndex}].contributors[${contribIndex}].id`,
+							c.id.toString(),
+						);
+						formData.append(
+							`albumSongs[${songIndex}].contributors[${contribIndex}].artistName`,
+							c.fullName,
+						);
+						formData.append(
+							`albumSongs[${songIndex}].contributors[${contribIndex}].role`,
+							c.role,
+						);
+					});
+				});
+				await axiosInstance.post("/musical-entity/publish/album", formData, {
+					headers: { "Content-Type": "multipart/form-data" },
+				});
+			} else {
+				formData.append("link", link);
+				appendContributors(formData, contributors, "contributors");
+				await axiosInstance.post("/musical-entity/publish/song", formData, {
+					headers: { "Content-Type": "multipart/form-data" },
+				});
+			}
 
 			toast.success(
@@ -612,5 +645,5 @@
 									{contributors.map((contributor) => (
 										<div
-											key={contributor.username}
+											key={contributor.id}
 											className="flex items-center gap-2 bg-[#282828] rounded-full py-1.5 px-3"
 										>
@@ -626,7 +659,5 @@
 											<button
 												type="button"
-												onClick={() =>
-													handleRemoveContributor(contributor.username)
-												}
+												onClick={() => handleRemoveContributor(contributor.id)}
 												className="text-gray-400 hover:text-red-400 transition-colors"
 											>
@@ -687,5 +718,5 @@
 													searchResults.map((artist) => (
 														<button
-															key={artist.username}
+															key={artist.id}
 															type="button"
 															onClick={() => handleAddContributor(artist)}
@@ -808,5 +839,5 @@
 												{song.contributors.map((contributor) => (
 													<div
-														key={contributor.username}
+														key={contributor.id}
 														className="flex items-center gap-2 bg-[#1a1a2e] rounded-full py-1 px-2.5 text-sm"
 													>
@@ -826,5 +857,5 @@
 																handleRemoveAlbumSongContributor(
 																	index,
-																	contributor.username,
+																	contributor.id,
 																)
 															}
@@ -902,5 +933,5 @@
 																		(artist) => (
 																			<button
-																				key={artist.username}
+																				key={artist.id}
 																				type="button"
 																				onClick={() =>
